Skip to content

Commit 0508c9a

Browse files
committed
Merge branch '3.4' into 4.1
* 3.4: Add docs for the WebLink component
2 parents c35b930 + 31f8116 commit 0508c9a

File tree

3 files changed

+217
-0
lines changed

3 files changed

+217
-0
lines changed

components/weblink.rst

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
.. index::
2+
single: WebLink
3+
single: Components; WebLink
4+
5+
The WebLink Component
6+
======================
7+
8+
The WebLink component provides tools to manage the ``Link`` HTTP header needed
9+
for `Web Linking`_ when using `HTTP/2 Server Push`_ as well as `Resource Hints`_.
10+
11+
Installation
12+
------------
13+
14+
.. code-block:: terminal
15+
16+
$ composer require symfony/weblink
17+
18+
Alternatively, you can clone the `<https://github.com/symfony/weblink>`_ repository.
19+
20+
.. include:: /components/require_autoload.rst.inc
21+
22+
Usage
23+
-----
24+
25+
The following example shows the component in action::
26+
27+
use Fig\Link\GenericLinkProvider;
28+
use Fig\Link\Link;
29+
use Symfony\Component\WebLink\HttpHeaderSerializer;
30+
31+
$linkProvider = (new GenericLinkProvider())
32+
->withLink(new Link('preload', '/bootstrap.min.css'));
33+
34+
header('Link: '.(new HttpHeaderSerializer())->serialize($linkProvider->getLinks()));
35+
36+
echo 'Hello';
37+
38+
Read the full :doc:`WebLink documentation </weblink>` to learn about all the
39+
features of the component and its integration with the Symfony framework.
40+
41+
.. _`Web Linking`: https://tools.ietf.org/html/rfc5988
42+
.. _`HTTP/2 Server Push`: https://tools.ietf.org/html/rfc7540#section-8.2
43+
.. _`Resource Hints`: https://www.w3.org/TR/resource-hints/

index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Topics
5353
testing
5454
translation
5555
validation
56+
weblink
5657
workflow
5758

5859
Best Practices

weblink.rst

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
Asset Preloading and Resource Hints with HTTP/2 and WebLink
2+
===========================================================
3+
4+
Symfony provides native support (via the :doc:`WebLink component </components/weblink>`)
5+
for managing ``Link`` HTTP headers, which are the key to improve the application
6+
performance when using HTTP/2 and preloading capabilities of modern web browsers.
7+
8+
``Link`` headers are used in `HTTP/2 Server Push`_ and W3C's `Resource Hints`_
9+
to push resources (e.g. CSS and JavaScript files) to clients before they even
10+
know that they need them. WebLink also enables other optimizations that work
11+
with HTTP 1.x:
12+
13+
* Asking the browser to fetch or to render another web page in the background;
14+
* Making early DNS lookups, TCP handshakes or TLS negotiations.
15+
16+
Something important to consider is that all these HTTP/2 features require a
17+
secure HTTPS connection, even when working on your local machine. The main web
18+
servers (Apache, Nginx, Caddy, etc.) support this, but you can also use the
19+
`Docker installer and runtime for Symfony`_ created by Kévin Dunglas, from the
20+
Symfony community.
21+
22+
Preloading Assets
23+
-----------------
24+
25+
Imagine that your application includes a web page like this:
26+
27+
.. code-block:: twig
28+
29+
{# templates/homepage.html.twig #}
30+
<!DOCTYPE html>
31+
<html>
32+
<head>
33+
<meta charset="UTF-8">
34+
<title>My Application</title>
35+
<link rel="stylesheet" href="/app.css">
36+
</head>
37+
<body>
38+
<main role="main" class="container">
39+
{# ... some content here ... #}
40+
</main>
41+
</body>
42+
</html>
43+
44+
Following the traditional HTTP workflow, when this page is served browsers will
45+
make one request for the HTML page and another request for the linked CSS file.
46+
However, thanks to HTTP/2 your application can start sending the CSS file
47+
contents even before browsers request them.
48+
49+
To do that, first install the WebLink component:
50+
51+
.. code-block:: terminal
52+
53+
$ composer req weblink
54+
55+
Now, update the template to use the ``preload()`` Twig function provided by
56+
WebLink:
57+
58+
.. code:: twig
59+
60+
<head>
61+
{# ... #}
62+
<link rel="stylesheet" href="{{ preload('/app.css') }}">
63+
</head>
64+
65+
If you reload the page, the perceived performance will improve because the
66+
server responded with both the HTML page and the CSS file when the browser only
67+
requested the HTML page.
68+
69+
.. tip::
70+
71+
Google Chrome provides an interface to debug HTTP/2 connections. Browse
72+
``chrome://net-internals/#http2`` to see all the details.
73+
74+
How does it work?
75+
~~~~~~~~~~~~~~~~~
76+
77+
The WebLink component manages the ``Link`` HTTP headers added to the response.
78+
When using the ``preload()`` function in the previous example, the following
79+
header was added to the response: ``Link </app.css>; rel="preload"``
80+
81+
According to `the Preload specification`_, when an HTTP/2 server detects that
82+
the original (HTTP 1.x) response contains this HTTP header, it will
83+
automatically trigger a push for the related file in the same HTTP/2 connection.
84+
85+
Popular proxy services and CDNs including `Cloudflare`_, `Fastly`_ and `Akamai`_
86+
also leverage this feature. It means that you can push resources to clients and
87+
improve performance of your apps in production right now.
88+
89+
If you want to prevent the push but let the browser preload the resource by
90+
issuing an early separate HTTP request, use the ``nopush`` option:
91+
92+
.. code:: twig
93+
94+
<head>
95+
{# ... #}
96+
<link rel="stylesheet" href="{{ preload('/app.css', { nopush: true }) }}">
97+
</head>
98+
99+
Resource Hints
100+
--------------
101+
102+
`Resource Hints <https://www.w3.org/TR/resource-hints/#resource-hints>`_ are
103+
used by applications to help browsers when deciding which resources should be
104+
downloaded, preprocessed or connected to first.
105+
106+
The WebLink component provides the following Twig functions to send those hints:
107+
108+
* ``dns_prefetch()``: "indicates an origin (e.g. ``https://foo.cloudfront.net``)
109+
that will be used to fetch required resources, and that the user agent should
110+
resolve as early as possible".
111+
* ``preconnect()``: "indicates an origin (e.g. ``https://www.google-analytics.com``)
112+
that will be used to fetch required resources. Initiating an early connection,
113+
which includes the DNS lookup, TCP handshake, and optional TLS negotiation, allows
114+
the user agent to mask the high latency costs of establishing a connection".
115+
* ``prefetch()``: "identifies a resource that might be required by the next
116+
navigation, and that the user agent *should* fetch, such that the user agent
117+
can deliver a faster response once the resource is requested in the future".
118+
* ``prerender()``: "identifies a resource that might be required by the next
119+
navigation, and that the user agent *should* fetch and execute, such that the
120+
user agent can deliver a faster response once the resource is requested later".
121+
122+
The component also supports sending HTTP links not related to performance and
123+
any link implementing the `PSR-13`_ standard. For instance, any
124+
`link defined in the HTML specification`_:
125+
126+
.. code:: twig
127+
128+
<head>
129+
{# ... #}
130+
<link rel="alternate" href="{{ link('/index.jsonld', 'alternate') }}">
131+
<link rel="stylesheet" href="{{ preload('/app.css', {nopush: true}) }}">
132+
</head>
133+
134+
The previous snippet will result in this HTTP header being sent to the client:
135+
``Link: </index.jsonld>; rel="alternate",</app.css>; rel="preload"; nopush``
136+
137+
You can also add links to the HTTP response directly from controllers and services::
138+
139+
// src/Controller/BlogController.php
140+
namespace App\Controller;
141+
142+
use Fig\Link\GenericLinkProvider;
143+
use Fig\Link\Link;
144+
use Symfony\Component\HttpFoundation\Request;
145+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
146+
147+
class BlogController extends AbstractController
148+
{
149+
public function index(Request $request)
150+
{
151+
$linkProvider = $request->attributes->get('_links', new GenericLinkProvider());
152+
$request->attributes->set('_links', $linkProvider->withLink(new Link('preload', '/app.css')));
153+
154+
return $this->render('...');
155+
}
156+
}
157+
158+
.. seealso::
159+
160+
WebLink can be used :doc:`as a standalone PHP library </components/weblink>`
161+
without requiring the entire Symfony framework.
162+
163+
.. _`HTTP/2 Server Push`: https://tools.ietf.org/html/rfc7540#section-8.2
164+
.. _`Resource Hints`: https://www.w3.org/TR/resource-hints/
165+
.. _`Docker installer and runtime for Symfony`: https://github.com/dunglas/symfony-docker
166+
.. _`preload`: https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content
167+
.. _`the Preload specification`: https://www.w3.org/TR/preload/#server-push-(http/2)
168+
.. _`Cloudflare`: https://blog.cloudflare.com/announcing-support-for-http-2-server-push-2/
169+
.. _`Fastly`: https://docs.fastly.com/guides/performance-tuning/http2-server-push
170+
.. _`Akamai`: https://blogs.akamai.com/2017/03/http2-server-push-the-what-how-and-why.html
171+
.. _`this great article`: https://www.shimmercat.com/en/blog/articles/whats-push/
172+
.. _`link defined in the HTML specification`: https://html.spec.whatwg.org/dev/links.html#linkTypes
173+
.. _`PSR-13`: http://www.php-fig.org/psr/psr-13/

0 commit comments

Comments
 (0)