Resource Hints & Prefetching

The Resource Hint Spectrum

Resource hints instruct the browser to perform network operations — from lightweight DNS lookups to full page prerendering — ahead of when it would naturally discover them. They form a spectrum from lightest to heaviest:

Hint What It Does Cost Use Case
dns-prefetch DNS resolution only Negligible (~1KB) Third-party domains you’ll use later
preconnect DNS + TCP + TLS handshake Low (~1-3KB overhead) Critical third-party origins (CDN, fonts, APIs)
preload Full resource download, current page Medium (resource size) Late-discovered critical assets (LCP image, fonts, CSS @import)
modulepreload Download + parse + compile JS module Medium-High Critical ES modules and their dependencies
prefetch Download resource for next navigation Low priority (idle bandwidth) Likely next-page assets
Speculation Rules Prefetch or prerender entire pages High (full page resources) High-probability next navigations

The key principle: preload and modulepreload are mandatory fetches — the browser will download the resource. The others are advisory hints that browsers follow at their discretion based on network conditions and available resources. Overusing mandatory hints wastes bandwidth and can delay more important resources.

dns-prefetch and preconnect: Warming Connections

dns-prefetch resolves a domain name to an IP address in advance, eliminating 20–120ms of DNS lookup latency. It’s the lightest hint with negligible overhead, making it safe to apply broadly to third-party domains:

<link rel="dns-prefetch" href="https://analytics.example.com">

preconnect goes further — completing DNS + TCP connection + TLS negotiation, saving 100–300ms on a typical connection. Use it for critical third-party origins where you know a request will follow:

<!-- Pair preconnect with dns-prefetch for fallback in older browsers -->
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
<link rel="dns-prefetch" href="https://cdn.example.com">

When to use which: preconnect for origins where you’ll definitely fetch resources within the next few seconds (your CDN, font host, API endpoint). dns-prefetch for domains that might be used (analytics, ad networks, links the user might click). Limit preconnect to 2–4 origins — each idle connection consumes device resources and may compete with active downloads.

The crossorigin attribute matters: CORS and non-CORS requests use separate connections. If you’re fetching both fonts (CORS) and images (non-CORS) from the same CDN, you need two preconnect hints — one with crossorigin and one without.

Resources:

preload: Fetch Critical Resources Early

preload is a mandatory, high-priority fetch for resources needed on the current page that the browser can’t discover early enough from HTML parsing alone. The most impactful use cases:

LCP images loaded via CSS (background-image) or deep in the DOM — the preload scanner can’t find these until the CSS is downloaded and parsed:

<link rel="preload" href="/images/hero.avif" as="image"
      type="image/avif" fetchpriority="high">

Fonts referenced in CSS — discovered only after the stylesheet is parsed:

<link rel="preload" href="/fonts/brand-latin.woff2"
      as="font" type="font/woff2" crossorigin>

CSS loaded via @import — the importing stylesheet must download before the imported one is discovered:

<link rel="preload" href="/css/critical-module.css" as="style">

Responsive image preloading with imagesrcset and imagesizes:

<link rel="preload" as="image" fetchpriority="high"
      imagesrcset="hero-400.avif 400w, hero-800.avif 800w, hero-1200.avif 1200w"
      imagesizes="(max-width: 600px) 100vw, 50vw"
      type="image/avif">

Critical rules for preload:

  • Always include the as attribute (image, font, style, script, fetch). Without it, the browser can’t prioritize correctly and may fetch the resource twice.
  • Add crossorigin for fonts (always required, even same-origin) and any CORS-fetched resource. Mismatched crossorigin causes a double download.
  • preload does not apply the resource — you still need the <link rel="stylesheet">, <img>, or <script> tag. Preload only starts the download earlier.
  • If a preloaded resource isn’t used within ~3 seconds, Chrome shows a console warning (“The resource was preloaded using link preload but not used”). This indicates wasted bandwidth.
  • Don’t over-preload. Each preloaded resource competes for bandwidth with everything else. Preload only 1–3 truly critical resources that are genuinely late-discovered.

Resources:

modulepreload: ES Module-Aware Loading

modulepreload is a specialized preload for JavaScript ES modules. It downloads the module, parses and compiles it, and places it in the module map — ready for instant execution. It also automatically fetches static import dependencies, preventing the sequential “waterfall” where each module must be downloaded and parsed before its imports are discovered:

<link rel="modulepreload" href="/js/app.js">
<link rel="modulepreload" href="/js/router.js">
<link rel="modulepreload" href="/js/utils.js">

Without modulepreload, a dependency chain like app.js → router.js → utils.js downloads sequentially — each file waiting for its parent to parse. With modulepreload, all three download in parallel and are pre-compiled, eliminating the sequential discovery penalty.

modulepreload is Baseline Newly Available across all modern browsers. The as attribute is not needed (the browser knows it’s a module). Use it for critical-path modules in your application’s entry point chain.

Resources:

fetchpriority: Fine-Grained Priority Control

The Fetch Priority API (fetchpriority attribute, Chrome 101+, Safari 17.2+, Firefox 132+) lets you hint at a resource’s relative priority: high, low, or auto (default). This is orthogonal to resource hints — it works on any <img>, <link>, <script>, or <iframe> element, as well as on fetch() calls.

The most impactful use: fetchpriority="high" on the LCP image. By default, images start at low priority. The browser may delay them behind stylesheets and scripts. Adding fetchpriority="high" promotes the LCP image to download during the first loading phase alongside critical CSS:

<img src="hero.avif" alt="Hero" fetchpriority="high"
     width="1200" height="600">

Conversely, fetchpriority="low" on non-critical images (carousels, below-fold thumbnails) prevents them from competing with LCP resources:

<!-- Carousel images that aren't immediately visible -->
<img src="slide-2.avif" alt="Slide 2" fetchpriority="low" loading="lazy">
<img src="slide-3.avif" alt="Slide 3" fetchpriority="low" loading="lazy">

For preload links, fetchpriority adjusts within the preload priority bucket — useful when you’re preloading multiple resources and want to signal which one matters most.

Resources:

prefetch and Speculation Rules: Preparing the Next Navigation

prefetch downloads resources for a future navigation at low priority during idle time. The browser fetches the resource and stores it in the HTTP cache. If the user navigates to that page, the prefetched resources load from cache — dramatically reducing load time:

<!-- Prefetch likely next page's CSS and JS -->
<link rel="prefetch" href="/next-page/bundle.js" as="script">
<link rel="prefetch" href="/next-page/styles.css" as="style">

Note: Safari does not support <link rel="prefetch"> as of 2026. For cross-browser speculative loading, use the Speculation Rules API.

The Speculation Rules API (covered in detail in Section 6) is the modern replacement for prefetch and the deprecated prerender. It provides declarative JSON-based rules for prefetching or prerendering entire pages based on URL patterns, link selectors, or eagerness levels:

<script type="speculationrules">
{
  "prerender": [
    {
      "where": { "href_matches": "/product/*" },
      "eagerness": "moderate"
    }
  ],
  "prefetch": [
    {
      "where": { "selector_matches": "a[href]" },
      "eagerness": "conservative"
    }
  ]
}
</script>

Speculation Rules support four eagerness levels: immediate (speculate right away), eager (speculate as soon as possible), moderate (speculate on hover for 200ms), and conservative (speculate on pointer-down/touch-start). The moderate and conservative levels are safest for most sites as they only speculate when the user shows clear intent to navigate.

Resources:

103 Early Hints: Resource Hints Before the HTML Arrives

103 Early Hints is the most powerful resource hint mechanism available. It’s an HTTP status code that allows the server to send Link headers with preload and preconnect hints before the final response (200 OK) is generated. This lets the browser start fetching critical resources during server “think time” — while the backend is running database queries, rendering templates, or processing business logic.

HTTP/1.1 103 Early Hints
Link: </css/main.css>; rel=preload; as=style
Link: </fonts/brand-latin.woff2>; rel=preload; as=font; crossorigin
Link: </images/hero.avif>; rel=preload; as=image
Link: <https://cdn.example.com>; rel=preconnect; crossorigin

HTTP/1.1 200 OK
Content-Type: text/html
...

The browser receives the 103 response and immediately starts fetching the hinted resources. By the time the 200 response with the HTML arrives, critical CSS, fonts, and the LCP image may already be downloaded or in-flight.

Real-world impact: Shopify measured 500ms+ faster LCP at P50 during Black Friday/Cyber Monday using Early Hints via Cloudflare. Akamai customers reported 30% LCP improvements. Tests by Arjen Karel showed LCP images appearing 35–45% faster with Early Hints. The improvement is most dramatic for uncacheable pages where the server needs time to generate the response.

Browser support: Chrome 103+, Firefox 126+ (preload + preconnect), Safari (preconnect only). The feature works with HTTP/2 and HTTP/3 (not HTTP/1.1).

Where to implement: CDN-level is easiest — Cloudflare (one-click enable), Akamai (Early Hints behavior), and Fastly all support 103 Early Hints. At the server level, NGINX 1.29+ supports proxying Early Hints from backends. For dynamic pages, the CDN or edge can send cached 103 responses from previous page loads while waiting for the fresh 200.

Best practices (validated by Shopify’s extensive analysis): hint 1–3 critical preloads (render-blocking CSS, LCP image, primary font) and 1–2 preconnects. More is not necessarily better — Shopify found that pages with 0–3 preload hints achieved the best performance, while over-hinting with 7+ resources showed diminishing or negative returns due to bandwidth contention. Focus on resources that are genuinely critical for initial render.

Resources:

Putting It Together: A Practical Hint Strategy

Here’s a template for a well-optimized <head> section combining resource hints:

<head>
  <!-- 1. Preconnect to critical origins (limit to 2-4) -->
  <link rel="preconnect" href="https://cdn.example.com" crossorigin>
  <link rel="preconnect" href="https://cdn.example.com"><!-- Non-CORS version for images -->
  <link rel="dns-prefetch" href="https://analytics.example.com">

  <!-- 2. Preload late-discovered critical resources (limit to 1-3) -->
  <link rel="preload" href="/fonts/brand-latin.woff2"
        as="font" type="font/woff2" crossorigin>
  <link rel="preload" href="/images/hero.avif"
        as="image" type="image/avif" fetchpriority="high">

  <!-- 3. Critical CSS (inlined or linked) -->
  <link rel="stylesheet" href="/css/critical.css">

  <!-- 4. Modulepreload for critical JS entry points -->
  <link rel="modulepreload" href="/js/app.js">
  <link rel="modulepreload" href="/js/router.js">

  <!-- 5. Prefetch likely next-page resources -->
  <link rel="prefetch" href="/next-page/bundle.js" as="script">

  <!-- 6. Speculation Rules for instant navigation -->
  <script type="speculationrules">
  {
    "prefetch": [
      { "where": { "selector_matches": "a[href]" },
        "eagerness": "conservative" }
    ]
  }
  </script>
</head>

The order matters: preconnects first (start connections early), preloads next (start critical downloads), then stylesheets, then modulepreloads, then lower-priority hints. And if your server/CDN supports 103 Early Hints, move the preconnects and preloads there for even earlier discovery.

Resources: