The browser is good at deciding on its own what to download and in what order, but it is not omniscient. It does not know that the heading font is declared at the bottom of a stylesheet, nor that the payment script is about to be loaded from an external domain. Resource hints exist to fill this gap. Used well they shorten loading; used badly they make it worse, because every priority given to one resource is taken away from another.
dns-prefetch: resolving the name in advance
<link rel="dns-prefetch" href="//api.external-service.example">
It asks the browser to resolve the domain name immediately, so that when it is really needed the DNS lookup is already done. It is the lightest hint: it costs very little and makes sense for domains that will be used later on the page, for example the one of a widget loaded after an interaction.
preconnect: opening the connection
<link rel="preconnect" href="https://external-fonts.example" crossorigin>
It goes further: it resolves DNS, opens the connection and completes the TLS negotiation. On mobile networks it can save several hundred milliseconds for the first resource from that domain. Two caveats:
- the
crossoriginattribute must match how the resource will be requested. Fonts are always fetched in CORS mode, so a preconnect to a font domain must usecrossorigin; otherwise the browser opens a connection it then cannot reuse; - connections opened and not used are closed after a few seconds and consume resources. It is best to limit yourself to two or three truly critical domains.
preload: fetching a specific resource right away
<link rel="preload" href="/fonts/headings.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/img/hero.avif" as="image" fetchpriority="high">
Preload orders the download of a specific resource with the priority appropriate to its type, given by as. It is useful for important resources the browser would discover late:
- fonts declared inside a CSS file;
- background images defined in CSS and used as the main element;
- scripts loaded dynamically by another script;
- the CSS of a component imported with
@import.
The typical mistakes are three. First: preloading without as, or with the wrong value, so the resource gets downloaded twice. Second: preloading resources that are not used within a few seconds; Chrome flags this with a console warning. Third: preloading too many things, turning preload into a way of telling the browser «everything is urgent», which amounts to saying nothing is.
Preloading responsive images
If the main image uses srcset, the preload must also know the variants, otherwise it downloads the wrong one:
<link rel="preload" as="image"
imagesrcset="/img/h-640.avif 640w, /img/h-1280.avif 1280w"
imagesizes="100vw">
fetchpriority: adjusting priority
The fetchpriority attribute accepts high, low or auto and applies to images, scripts, links and fetch() calls. It does not change when the browser discovers the resource, but how important it considers it compared with the others. The most effective uses:
| Case | Hint |
|---|---|
| LCP image | fetchpriority="high" on the img tag |
| Carousel images not yet visible | fetchpriority="low" |
| Non-essential async script | fetchpriority="low" |
| API call feeding the main content | fetch(url, { priority: "high" }) |
For images the benefit is often obvious: by default the browser gives images low priority until layout confirms they are visible, and only then raises it. Setting high on the main image from the start removes that wait.
modulepreload for module-based JavaScript
For applications that use ES modules there is a specific variant:
<link rel="modulepreload" href="/js/app.js">
Besides downloading the file, the browser parses it and, depending on the implementation, may also fetch its dependencies early, shortening the import chain.
How to tell whether you need them
The method is always the same: look at the waterfall. If a critical resource starts late because it is discovered late, preload helps. If it starts early but sits in the queue for a long time with low priority, you need fetchpriority. If it shows a long connection phase to an external domain, consider preconnect. After each change, measure again: it happens that preloading a font speeds up the heading but slows the main image, because the two resources compete for the same bandwidth.
In short
dns-prefetchresolves the name in advance, costs little and suits domains used later.preconnectopens the connection and TLS; limit it to a few domains and respectcrossorigin.preloadis for critical resources discovered late; always specifyas.fetchpriorityadjusts relative importance and is very useful on the LCP image.- Every hint takes priority away from something else: always check the effect in the waterfall.