~ 5 min read

Instant Page Loads with Chromium Prerendering & Speculation Rules API


Chrome has brought back full page prerendering, allowing near-instant navigation with the Speculation Rules API. Here’s everything you need to know.

What is Prerendering?

Prerendering loads an entire page in an invisible background tab before the user clicks. When activated, the page appears instantly—often achieving zero LCP, reduced CLS, and better INP.

The old <link rel="prerender"> was replaced by NoState Prefetch (which only fetched resources). Now Chrome has full prerendering back, via the Speculation Rules API.

How Pages Get Prerendered

Four ways Chrome prerenders pages:

  1. Address bar predictions - When typing in the omnibox with >50% confidence
  2. Bookmark hover - Holding pointer over bookmark buttons
  3. Search engines - When instructed by search results pages
  4. Speculation Rules API - Sites tell Chrome which pages to prerender

The Speculation Rules API

Add JSON rules to tell Chrome what to prerender:

URL Lists

<script type="speculationrules">
  {
    "prerender": [
      {
        "urls": ["next.html", "next2.html"]
      }
    ]
  }
</script>

Document Rules

Match links automatically with selectors:

<script type="speculationrules">
  {
    "prerender": [
      {
        "where": {
          "and": [
            { "href_matches": "/*" },
            { "not": { "href_matches": "/wp-admin" } },
            { "not": { "selector_matches": ".no-prerender" } }
          ]
        }
      }
    ]
  }
</script>

Eagerness Levels

Control when speculation fires:

  • immediate - Prerender as soon as rules are observed (default for URL lists)
  • eager - Desktop: 10ms hover; Mobile: 50ms after anchor enters viewport
  • moderate - Desktop: 200ms hover or pointerdown; Mobile: 500ms after scroll stops (default for document rules)
  • conservative - Only on pointer/touch down

Recommended starter rule:

<script type="speculationrules">
  {
    "prerender": [
      {
        "where": { "href_matches": "/*" },
        "eagerness": "moderate"
      }
    ]
  }
</script>

Prefetch Instead

Just fetch without full prerender:

<script type="speculationrules">
  {
    "prefetch": [
      {
        "urls": ["next.html"]
      }
    ]
  }
</script>

Unlike old <link rel="prefetch">, this processes documents properly and holds them in memory.

Chrome Limits

PrefetchPrerender
immediate5010
eager/moderate/conservative2 (FIFO)2 (FIFO)

Chrome won’t speculate when:

  • Save-Data mode enabled
  • Energy saver on low battery
  • Low memory
  • “Preload pages” setting disabled
  • Background tabs

Implementation

Static (in HTML)

<head>
  <script type="speculationrules">
    {
      "prerender": [
        {
          "where": { "href_matches": "/*" },
          "eagerness": "moderate"
        }
      ]
    }
  </script>
</head>

Dynamic (via JavaScript)

if (HTMLScriptElement.supports?.('speculationrules')) {
  const specScript = document.createElement('script');
  specScript.type = 'speculationrules';
  specScript.textContent = JSON.stringify({
    prerender: [
      {
        urls: ['/next.html'],
      },
    ],
  });
  document.body.append(specScript);
}

Via HTTP Header (Chrome 121+)

Speculation-Rules: "/speculationrules.json"

JSON file must have correct MIME type:

Content-Type: application/speculationrules+json

Detecting Prerender

In JavaScript

// Check if currently prerendering
if (document.prerendering) {
  // Delay analytics/side effects
}

// Wait for activation
document.addEventListener('prerenderingchange', () => {
  // Page is now visible
});

// Check if page was prerendered
const wasPrerendered = performance.getEntriesByType('navigation')[0]?.activationStart > 0;

Server-Side

Prerendered requests include:

Sec-Purpose: prefetch;prerender

Return non-2XX to prevent prerender (though better to allow it and delay actions via JS).

Analytics Impact

Important: Delay analytics until activation to avoid counting prerendered pages as views.

const whenActivated = new Promise((resolve) => {
  if (document.prerendering) {
    document.addEventListener('prerenderingchange', resolve, { once: true });
  } else {
    resolve();
  }
});

async function initAnalytics() {
  await whenActivated;
  gtag('config', 'G-12345678-1');
}

Google Analytics, Google Publisher Tag, and Adsense handle this automatically.

Track Prerender Success

// Custom dimension for prerendered navigations
const wasPrerendered = self.performance?.getEntriesByType('navigation')[0]?.activationStart > 0;

gtag('set', { dimension1: wasPrerendered });

Restrictions & Gotchas

  • Same-origin by default - Cross-origin prerender needs Supports-Loading-Mode: credentialed-prerender header
  • Cross-origin prefetch - Works for same-site, or different-site without cookies
  • No SPAs - Only full page navigations, not soft navigations
  • Content Security Policy - Need hash/nonce for speculation rules, or use inline-speculation-rules

Advanced Features

Target Hint (Chrome 138+)

Prerender for target="_blank" links:

<script type="speculationrules">
  {
    "prerender": [
      {
        "target_hint": "_blank",
        "urls": ["next.html"]
      }
    ]
  }
</script>

No-Vary-Search (Chrome 141+)

Reuse prerenders despite different URL parameters:

No-Vary-Search: params=("utm_content")
<script type="speculationrules">
  {
    "prefetch": [
      {
        "urls": ["/products"],
        "expects_no_vary_search": "params=(\"id\")"
      }
    ]
  }
</script>

Canceling Speculations

Remove speculation rules to cancel, or use HTTP header:

Clear-Site-Data: prefetchCache, prerenderCache

Useful after state changes (login, add-to-cart).

Best Practices

  1. Start with prefetch - Test before full prerender
  2. Use moderate eagerness - Good balance for most sites
  3. Exclude admin pages - Use href_matches to filter out /admin, /checkout
  4. Watch your limits - Only 2 hover-based prerenders at a time
  5. Delay side effects - Wait for activation before analytics, state updates
  6. Test thoroughly - Use Chrome DevTools Application panel to debug speculation rules
  7. Monitor hit rate - Track how many prerenders actually get used

Debugging

Chrome DevTools shows speculation rules in Application > Speculative loads panel. Test with:

// Console check
performance.getEntriesByType('navigation')[0].activationStart;
// Non-zero = was prerendered

View Chrome’s predictions:

chrome://predictors

Browser Support

Checkout Can I Use for the latest support status. As of now, it’s only in Chromium-based browsers (Chrome, Edge, Opera) and not in Firefox or Safari.

Real Impact

Sites see near-instant page loads. Even fast sites benefit from:

  • Near-zero LCP (already loaded!)
  • Reduced CLS (load CLS happens before view)
  • Better INP (page loaded before interaction)

The cost is memory and bandwidth—use wisely. Don’t over-prerender.

Quick Start

Add this to start prerendering same-origin links on 200ms hover:

<script type="speculationrules">
  {
    "prerender": [
      {
        "where": { "href_matches": "/*" },
        "eagerness": "moderate"
      }
    ]
  }
</script>

Then refine based on analytics and hit rates.


Resources:


Headshot of Nam Le

Hi, I'm Nam Le. I'm a software engineer based in HCM city, Vietnam. You can follow me on Twitter, see some of my work on GitHub, or connect with me on LinkedIn.