Tags: web-dev concept

Resource Hints

Date: 2026-08-17


Telling the browser to start work earlier than it otherwise would — resolving a DNS name, opening a connection, fetching a file. Each one buys time by spending bandwidth and connection slots, so a page that hints at everything has prioritised nothing and is usually slower than one that hints at nothing.


Resource hints are <link rel> values — dns-prefetch, preconnect, preload, prefetch — that tell the browser to start a lookup, connection or download before it would discover the need itself.

The four, by how much they commit

dns-prefetch   resolve the hostname                        ~20–120ms saved
     ↓         cheapest. no connection opened.

preconnect     DNS + TCP handshake + TLS negotiation       ~100–300ms saved
     ↓         a full connection, held open and idle
               if unused, you've paid for a socket for nothing

prefetch       download a file, low priority, for a         a whole request
     ↓         FUTURE navigation
               wasted entirely if they don't navigate there

preload        download a file NOW, high priority, for      a whole request,
               THIS page                                     competing with
               the browser still decides when to USE it      real traffic

They’re not a ladder of strength — they answer different questions. preconnect is about where you’ll fetch from; preload is about what you’ll fetch; prefetch is about the next page.

The syntax, and the attribute people forget

<!-- warm the connection to a font or image host -->
<link rel="preconnect" href="https://fonts.example.com" crossorigin>
 
<!-- cheaper, for hosts you'll use but not immediately -->
<link rel="dns-prefetch" href="https://analytics.example.com">
 
<!-- fetch the hero image now, at high priority -->
<link rel="preload" as="image" href="/hero.avif" type="image/avif">
 
<!-- fonts ALWAYS need crossorigin, even same-origin -->
<link rel="preload" as="font" type="font/woff2"
      href="/fonts/inter.woff2" crossorigin>
 
<!-- the likely next page, at idle priority -->
<link rel="prefetch" href="/checkout" as="document">

Two attributes cause most of the wasted hints:

  • as is mandatory on preload. Without it the browser can’t set the right priority or apply the right Accept header, and it will often fetch the file twice — once as the preload and once as the real request. A preload that double-fetches is worse than no preload
  • crossorigin on fonts, always — including same-origin fonts. Font requests are made in anonymous CORS mode, so a preload without crossorigin doesn’t match the actual request and downloads separately

Where each earns its place

preconnect — for a third-party origin on the critical path that the browser can’t discover early, most often a font host, an image CDN or a payment iframe.

without preconnect              with preconnect

HTML parsed        0ms          HTML parsed          0ms
…                               preconnect starts    5ms
font CSS found   180ms          font CSS found     180ms
  DNS             30ms            connection ready
  TCP             40ms            (already warm)
  TLS             60ms
  request        120ms          request            120ms
                 ─────                             ─────
                 430ms                             300ms

Limit it to two or three origins. Each held connection consumes a socket and competes for bandwidth during the most contended moment of the page load, and an unused preconnect is pure cost. The browser closes idle preconnects after around ten seconds, so hinting to something you use late is wasted.

preload — for a critical resource the browser discovers late, which in practice means:

  • A font referenced from a CSS file (found only after the CSS downloads and parses)
  • The LCP image when it’s set via CSS background-image rather than an <img> tag
  • A critical script loaded by another script

preload is pointless for things the preload scanner already finds early — an <img src> near the top of the HTML, a <script src> in the head. The browser found those before your hint mattered. This is the commonest wasted hint by a wide margin.

prefetch — where the next navigation is genuinely predictable. Product page → basket, basket → checkout, step 1 → step 2. On a mobile connection, prefetching a page nobody visits spends someone’s data allowance for nothing, so keep it to high-confidence paths.

The harm of over-hinting

a page preloading 14 resources

  → all 14 arrive at "high" priority
  → the browser's own prioritisation is overridden
  → the LCP image now competes with 13 other high-priority requests
  → LCP gets WORSE than with no hints at all

Priority is relative. If everything is important, the browser has lost the information it uses to sequence work — and its heuristics are good, so overriding them needs a specific reason.

Symptoms of over-hinting worth checking for:

  • Console warnings about a preloaded resource unused within a few seconds — the browser tells you directly
  • Double-fetches in the network panel, usually from a missing as or a crossorigin mismatch
  • LCP regressing after adding hints, which is the diagnostic that matters

Adjacent levers that solve nearby problems, and are often the lighter-touch fix:

  • fetchpriority="high|low|auto" — reorders a known request without adding one. Try this before reaching for preload — Priority Hints
  • loading="lazy" — defers off-screen images. Never on the LCP element, which is a common self-inflicted regression — Lazy Loading
  • decoding="async" — moves image decode off the main thread
  • The Link: HTTP header — the same hints delivered in the response headers, so they arrive before the HTML body is parsed. Earlier still, and useful when serving from a CDN — CDN Caching

Checking it worked

DevTools → Network, sorted by start time. A successful preload moves the resource earlier in the waterfall; two entries for the same file means a mismatched as or a missing crossorigin — DevTools.

Lighthouse flags both “preload key requests” and unused preloads. The second warning is the more useful one, since it catches hints added once for a resource that no longer exists — which is how a page ends up with four hints and three of them wasted.

The rules

  • Measure before and after, in the field. Hints are a reallocation of a fixed bandwidth budget, so a local test on fast fibre shows almost nothing — Field vs Lab Data, Real User Monitoring
  • Three or four hints total on a typical page. If you need more, the problem is the number of critical resources, not the hinting — The Critical Rendering Path
  • Fix discovery before hinting. Moving a font declaration into the HTML beats preloading it from a CSS file
  • Server push is not the answer. HTTP/2 push was the earlier attempt at this problem and has been retired by browsers; preload is the surviving mechanism — HTTP Versions

Where it interacts

  • Priority Hints — the finer control, for when the browser knows about a resource and gets its importance wrong
  • Render-Blocking Resources — hints reorder work; removing blockers reduces it, and reduction wins
  • Font Loading — the single most common legitimate preload, and where the crossorigin trap bites
  • Third-Party Scripts — preconnect is a cheap partial mitigation for third-party latency you can’t otherwise control