HTTP Link headers have a long history, yet are only recently becoming a valuable tool to improve website performance. What they do and what you can tell a browser, web server or CDN to do has changed a lot and is still currently changing, which is why I want to explain the past, present and future of this header and how you can use it right now.

When sending back a response to a browser there are usually quite a few additional resources the browser will need in order to render a website: CSS stylesheets, Javascript files and images, and possibly more. This was one of the intentions of introducing Link as a HTTP header, to give the browser a heads-up about additional resources or other linked documents related to the current one.

This corresponds to the link HTML tag that is still heavily used today to tell the browser about stylesheets, favicons, manifest files and many more possible resources the browser might want to know about. Yet even though the HTTP Link header has existed as a concept since at least 1999 it does not seem to have been used by developers or browsers in real life - even as a heavily performance and usability-focused developer I didn’t come across it before 2017, when its intent had already changed.

These would be a few examples of how the headers would have been used, which is not quite the same syntax as today:

Link: <contrast.css>; rel="stylesheet alternate"; title="High Contrast Styles"; type="text/css"; media="screen",<http://www.cern.ch/TheBook/chapter2>; rel="Previous"

In 2015 with the release of HTTP/2 an optional feature of it was Server Push, which started being supported in 2017/2018 by CDNs and web servers. Server Push used the Link headers to push additional content to the browser, with the following syntax:

Link: <styles.css>; rel=preload; as=style,<scripts.js>; rel=preload; as=script

The web server or CDN would recognize these Link headers with “rel=preload” and push the referenced resources back to the client together with the response. Because of the automatic push, it reduced the amount of round trips between client and server, as the client received content without first having to request it.

There were a few issues with this mechanism though:

  • If the client already had the resources in cache, there was a good possibility unnecessary resources were transferred (Server Push can be cancelled by the client, but at that point it might already be too late)
  • How does the application know what resources the client has in cache? The reality was that there is no mechanism to find out, so over-pushing was even more likely
  • Only pushing resources to new clients where you know they don’t have the resources yet is hard to implement and is impossible if you do not keep track of your visitors
  • If too much is pushed, performance can be worse compared to Server Push not being used at all
  • Only available resources on the server can be pushed, external content and connections do not benefit from Server Push

These reasons are why Server Push has been a disappointment: Hardly any applications have implemented it. I have implemented it for one application, by only pushing basic resources if a new session is started and I assume the client is new.

For browsers and web servers this kind of pushing behavior is complex, as additional requests are exchanged and have to be handled in addition to the original request. This is why the Chrome web browser is planning on removing HTTP Server Push, to remove the complexity around it and replace it with HTTP Early Hints.

We have now reached the present and future, where the Link header is slated to be used for a special HTTP 103 response. This would be an example of how to declare the HTTP Link header:

Link: <styles.css>; rel=preload; as=style,<scripts.js>; rel=preload; as=script

As you can see, it is exactly the same as with HTTP Server Push, although we can add nopush to make sure we do not do Server Push (I do find the similar syntax yet different behavior unfortunate):

Link: <styles.css>; rel=preload; as=style; nopush,<scripts.js>; rel=preload; as=script; nopush

For external connections, you can add those as preconnect entries, meaning the browser will open a connection in preparation for sending a request there later:

Link: <styles.css>; rel=preload; as=style; nopush, <https://cdn.test>; rel=preconnect

With Early Hints a web server or CDN can emit an early response before sending back the final response, which would look a bit like this:

  • The client sends a request to the web server
  • The web server sends back a HTTP 103 response with possible resources the browser could already prepare/download
  • Later, the web server sends back the regular HTTP response

This enables the client to download stylesheets, scripts and images early on, and even connect to additional domains for external resources. As soon as the final response arrives, it is processed as usual, but the prepared resources are already in place and can be used immediately, speeding up the experience for the client. If the client already has resources cached, it can ignore them, avoiding the unnecessary downloads that were likely to happen with Server Push.

A CDN like Cloudflare can take extra advantage of Early Hints because it can cache Link headers and use them for the Early Hints responses even before relaying the request to the origin server. A regular web server could do the same. The Link headers in the regular response are even useful without Early Hints, as a browser will know to download those resources before parsing the HTML, although the gains there are significantly less.

The bad news is that no browser, web server or CDN currently supports Early Hints. Cloudflare has started an Early Hints beta, although their article has quite a few inaccuracies - they state they never supported Server Push, although they have for years and still do, so take their article with a grain of salt. Chrome/Chromium has added experimental Early Hints support to their development releases, so it will show up in an official version sooner or later, and all Chromium-based browsers will then also get it. So it might only be a few months until Early Hints has some actual impact on performance.

There are no downsides to implementing Link HTTP headers right now - even without Early Hints support they can benefit performance by telling the browser what to download, and as soon as Early Hints support appears your website will automatically be even faster! Just use the following syntax:

Link: <styles.css>; rel=preload; as=style; nopush,<scripts.js>; rel=preload; as=script; nopush, <https://cdn.test>; rel=preconnect

You can include any assets in the Link header that will definitely be used on the page: CSS and JS are obvious candidates, but a SVG logo or some commonly used icons on the page might be a good idea too, as well as preconnect entries for any domains you know you will connect to.