Common problems with rel="preload"

16 Mar 2021

Preload <link> tags are a type of browser resource hint, telling the browser to start downloading a file with a high priority.

You can use preload tags to make your site load faster, but when used incorrectly they can also slow it down. This article highlights common mistakes when using preload hints, and explains how to avoid them.

How to use preload correctly

First, let's look at how preload is supposed to work.

This WebPageTest waterfall shows a common performance problem that can be solved using preload. A CSS file references a font, but this font doesn't start loading until the CSS file has been fetched.

Sequential requests without preload

By adding a preload tag to the document, the browser can start loading the font and the CSS file at the same time. You can see this on the Shopify homepage, where the fonts are preloaded to make sure the page quickly renders with the correct fonts.

Parallel requests with preload

Preloading too many resources

Preload tags can help the browser prioritize important requests. But preloading a lot of files can actually make the prioritization worse, the your page will load more slowly.

Take a look at this example from the Asana homepage. The green line in the request waterfall shows when the page starts rendering.

Asana making a large number of requests before the page starts to render

It looks like all of these JavaScript files are render-blocking. But actually, the page contains 26 preload tags.

Instead of loading the important render-blocking resources, Chrome focusses on a large number of low-priority files.

Preloading unused resources

If a preloaded file isn't used Chrome will show a message like this:

The resource https://fonts.googleapis.com/css2?family=Ranchers was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate as value and it is preloaded intentionally.

There are a few common causes of this:

  • using preload (high-priority) when you meant prefetch (low-priority)
  • removing a resource from a page, but not removing the corresponding preload tag
  • preloading resources loaded by a third-party script, but then the third-party stops using that resource

Deprioritizing important resources

This waterfall shows a site with a large render-blocking CSS file and a preloaded JavaScript file that's used later on (angular.js).

The preload is competing with the render-blocking file for bandwidth. As a result, the download takes longer and the page renders more slowly.

CSS and JavaScript files competitng for bandwidth

The page renders 0.9s faster without the preload.

CSS file loading before JavaScript

The downside of this change is that the JavaScript file will now finish loading later. Depending on the relative importance of the initial render compared to the full app load this may be fine, or you might actually want to intentionally prevent rendering until the app code has loaded.

One simple way to speed up this example is using a preconnect tag. This makes sure that the browser establishes a server connection, but does not start consuming bandwidth downloading the resource.

Preconnecting to the server hosting the JavaScript file

If the app code is important, but less important than the initial render, consider lazy-loading other content like images. That way bandwidth will first be used to load the render-blocking CSS file, then to load the app code, and finally to load the images.

CORS mode mismatch

Preload can prioritize resource loading by starting requests early, but this only works if the subsequent browser request matches the preload request. (Thanks to Jakub for bringing up this issue on Twitter.)

This is especially common with fonts, which are always loaded using anonymous mode CORS.

Let's say this is your preload tag:

<link
  rel="preload"
  href="https://fonts.gstatic.com/s/ranchers/v8/zrfm0H3Lx-P2Xvs2ArDfBi_fXiHu.woff2"
  as="font">

If you look at the request waterfall you can see that the font is actually loaded twice.

(Using a DebugBear waterfall here, as WebPageTest seems to not show the second request. DevTools also shows it correctly though.)

Font is loaded again and the preloaded response is unused

The console will show a warning like this:

A preload for 'https://fonts.gstatic.com/s/ranchers/v8/zrfm0H3Lx-P2Xvs2ArDfBi_fXiHu.woff2' is found, but is not used because the request credentials mode does not match. Consider taking a look at crossorigin attribute.

Adding the crossorigin attribute to the link tag ensures that both requests are made using CORS headers. The preload response can then be safely reused for the actual font load.

Preloaded font is used, not second request

DebugBear is a website monitoring tool built for front-end teams. Track performance metrics and Lighthouse scores in CI and production. Learn more.

Get new articles on web performance by email.

DebugBear logo
Track and analyze site speed with DebugBear.
➔ Learn more