Link Headers, Fetch Priority and Early Hints: A Performance Trifecta?

Tyron Goldschmidt
Dotdash Meredith Tech Blog
6 min readSep 14, 2023

--

By Tyron Goldschmidt and Joseph Freeman

Down to the Milliseconds

We pursue extremely fast websites at Dotdash Meredith, and experiment with new optimizations at every level of the stack. This article is about just three features — three somewhat related features — that can improve frontend performance: Link Headers, Fetch Priority and Early Hints. You might be interested in these features too, since speed means better user experience, more user engagement, better SEO, and more revenue.

Link Headers

The browser needs to know about various resources: styling, font, icons, scripts and what not. We can use the link tag to tell the browser what to load, adding the tag to the DOM (the Document Object Model, the full HTML of the page) either in the<head> or <body> of a given page. We can also recommend that the resources load at the most opportune time (covered later), and we can even preload them before the DOM is parsed. One way to do so is via Link Headers: Link tags can be included in the initial HTML HTTP response headers to give reference to resources and preload content.

Before we illustrate Link Headers in more detail, let’s first mention the more familiar <link> tag. When the <link> tag is in the <head> of the document we can add a rel=preload attribute which informs the browser to possibly load a resource early on that will be needed later. Maybe you’d like to preload a JavaScript file needed in the footer? Try using:<link rel=“preload” href=“footer-script.js” as=“script” />.

Link Headers take this feature a step further. Instead of informing the browser in the <head> of the document — which requires the document to be downloaded and parsed before the browser is aware of the resource — we can inform the browser in the response to the initial HTML request itself as a “Link:” header.

For example, let’s mix things up with a preload for Google Analytics:

HTTP 200 OK
Link:<//www.google-analytics.com/analytics.js>; rel=preload; as=script; nopush

Now the browser can immediately load the resource; it doesn’t need to wait until the document is in memory. Compare the difference when we preload google-analytics.js, alongside gtm.js and gpt.js. Before the Link Header, google-analytics.js was so far down the load order that it hadn’t even appeared on screen yet on the left hand side:

Putting render-blocking scripts there would speed up their arrival, along with whatever is blocked by them. Putting the css and fonts there would help decrease the chance of the notorious flash of unstyled content (FOUC), and help avoid a cumulative layout shift (CLS). Putting the hero image into a Link Header would mean an improvement to Largest Contentful Paint (LCP). Link Headers maximize bandwidth, and are going to be hard to beat. But there’s no harm in trying.

Onto Fetch Priority then.

Fetch Priority

Fetch Priority is the artist formerly known as Priority Hints. Not to be confused with Early Hints (on which see later).

The browser has priority defaults for resources, and makes internal calculations for the priority of resources that need to be downloaded. Fetch Priority gives developers a little more control over the priority for resources. Not absolute control, since it is just a hint and may be ignored, and it will definitely be ignored on some browsers.

You can provide a resource a priority as simply as this:

<image src="low-priority-image" fetchpriority="low" />

Why would you want to lower the priority of a resource? Perhaps the resource is arriving before it’s really needed. By loading before needed, it’s delaying the loading of more crucial assets. So in this case we would use fetchpriority=“low”.

In our case we tried to increase the priority of an image in a few ways, particularly to improve the LCP score. A closer look showed that our image already had a “high” priority. What kind of mischief is this? Take a look at this network waterfall. Note also that the hero image of the yawning cat downloads there in the red circle, at the same time as a very minor image:

We then added an explicit fetchpriority=“high” to the hero inline image. There were no discernible changes to the waterfall. We then added a fetchpriority=“high” to the preload for the image in the Link Header (recall what that is?) Again, no discernible changes. Then we removed the preload Link Header for the image and added the fetchpriority=“high” inline. Finally, we saw significant changes:

The hero image now downloads sooner. The very minor image mentioned earlier is left in the dust. We had a 50–150ms improvement in LCP (Largest Contentful Paint) on fast 3G networks. There was consistent but slight deterioration in FCP (First Contentful Paint). Still, the benefits outweigh the costs.

Early Hints

Early Hints are not to be confused with Priority Hints (aka Fetch Priority). The Early Hints 103 status code allows the server to send resources hints before the final 200 status response. This can be used by the client to fetch resources in parallel and, as a result, reduce page load time. Note: Early Hints are not used by browsers widely yet and have some other limitations. For example, since we’re using the CDN to serve Early Hints, they are hard to manage on resources varying across pages or changing overtime — we’d have to set up individual hints in the CDN for each URL and coordinate changes across the codebase and CDN to reduce the risk of mismatches.

Here’s how your Early Hints response header might look in the the CDN:

103 Early Hint
Link: <//style.css>; rel=preload; as=style; nopush,
Link: <//script.js>; rel=preload; as=script; nopush,
Link: <//yawning-cat.jpg; rel=preload; as=image; nopush

200 OK
Link: …

They likely won’t be seen in the response headers in your Network tab, unless there’s been some update to the Network tab since writing this article. But you can observe the creature with Chrome’s net-export tool: chrome://net-export.

And here’s how the waterfall was affected in our case (we’re keeping you on your toes with a vertical arrangement):

Big changes, but that did not result in any big gain in Core Web Vitals. In fact they did not result in any gains at all. If your servers are already fast, that might not be too surprising. But if your servers are slower, Early Hints might be of more use!

Conclusion

We experienced a big leap in performance with Link Headers, a little extra hop with Fetch Priority, and not even a waddle with Early Hints. That’s not to say that these will affect the performance of your sites in the same way. It’s worth experimenting with each of these — and sometimes experimenting in weird ways (!) — independently and together to see what surprises are in store and what might work for you.

--

--