Speculation Rules API — Achieve Near-Instant Page Navigations

Posted on: 4/24/2026 9:14:53 PM

1. The Problem: Why Web Navigation Is Still Slow

Have you ever clicked a link on a website and waited 1-3 seconds for the new page to appear? In an era where native apps switch screens almost instantly, web navigation remains a major weakness. Every time a user clicks a link, the browser must perform a chain of tasks: DNS lookup → TCP/TLS handshake → fetch HTML → parse → load CSS/JS → render. All of this happens after the user clicks.

Single Page Applications (SPAs) partially solve this by loading JavaScript once and rendering on the client. But SPAs bring their own issues: large bundle sizes, complex SEO, and slow initial load times. For Multi-Page Applications (MPAs) — which make up the majority of the web — slow navigation remains an open problem.

53%Users abandon pages that take > 3 seconds
100msHuman perception threshold for "instant"
~0msLCP when prerender is properly used
Chrome 121+Full Speculation Rules support

2. What Is the Speculation Rules API?

The Speculation Rules API is a Web API that lets developers instruct the browser to prefetch or prerender pages that users are likely to visit next. Unlike legacy prefetch solutions (<link rel="prefetch">), the Speculation Rules API can fully prerender an entire page in a hidden background tab — when the user clicks, the page is "activated" nearly instantly.

Think of it like a restaurant kitchen: instead of waiting for customers to order before cooking, the kitchen prepares popular dishes in advance. When a customer orders one of those dishes, it's served immediately — zero wait time.

graph LR
    A[User lands
on Page A] --> B{Speculation Rules
analyze links} B --> C[Prefetch Page B
Load HTML + resources] B --> D[Prerender Page C
Full render in hidden tab] C --> E[User clicks B
50-80% faster render] D --> F[User clicks C
Instant display ~0ms] style A fill:#e94560,stroke:#fff,color:#fff style B fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style C fill:#2c3e50,stroke:#fff,color:#fff style D fill:#2c3e50,stroke:#fff,color:#fff style E fill:#4CAF50,stroke:#fff,color:#fff style F fill:#4CAF50,stroke:#fff,color:#fff
Speculation Rules API flow: Prefetch vs Prerender

Important

The Speculation Rules API is designed for Multi-Page Applications (MPAs). For SPAs, client-side routing already handles navigation, so this API provides minimal benefit. If you're building with server-side rendering (Nuxt, Next.js SSR, ASP.NET MVC, Laravel...), this is a game-changer for user experience.

3. Prefetch vs Prerender: Two Levels of Optimization

The Speculation Rules API provides two primary actions, each suited for different situations:

Criteria Prefetch Prerender
What it does Fetches the HTML document + some subresources Fetches and fully renders the page in a hidden tab
Resource cost Low — network requests only High — CPU + RAM + network
Navigation speed 50-80% faster (still needs rendering) Near-instant (~0ms LCP)
JavaScript execution Not executed Fully executed
Scope of use Broad — many pages at once Narrow — only high-probability pages
Cross-origin Supported Same-origin only (by default)
Waste risk Low Medium to high if predictions are wrong

Practical advice

Start with prefetch for most links on the page (eagerness: moderate). Only use prerender for pages with high visit probability — e.g., the next step in a wizard, a prominent "Read more" link, or a hero CTA landing page. Prefetch has virtually zero risk; prerender requires careful consideration.

4. JSON Syntax and Implementation

Speculation rules are declared using JSON inside a <script type="speculationrules"> tag. There are two rule types: URL List (explicit URL enumeration) and Document Rules (condition-based URL selection from the current document).

4.1 URL List Rules — Explicit Declaration

The simplest approach: explicitly list the URLs to speculate.

<script type="speculationrules">
{
  "prerender": [
    {
      "urls": ["/about", "/contact", "/blog"],
      "eagerness": "moderate"
    }
  ],
  "prefetch": [
    {
      "urls": ["/products", "/pricing", "/docs"],
      "eagerness": "eager"
    }
  ]
}
</script>

Best when you know exactly which page users will visit (e.g., the next step in a checkout flow).

4.2 Document Rules — Condition-based URL Selection

Far more powerful: the browser automatically finds links in the document matching your specified conditions.

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

Here, the browser will prerender all internal links, except the login page, admin area, and links with the .no-prerender class.

5. Document Rules — Smart Conditional Prerendering

Document Rules are the most powerful feature of the Speculation Rules API. Instead of manually listing URLs, you describe conditions and let the browser select matching URLs from the current page's links.

5.1 href_matches Conditions

Use URL patterns to match URLs:

{
  "prefetch": [
    {
      "where": {
        "href_matches": "/blog/:slug"
      },
      "eagerness": "moderate"
    }
  ]
}

This rule will prefetch all links matching /blog/post-slug when the user hovers for 200ms.

5.2 selector_matches Conditions

Use CSS selectors to target links:

{
  "prerender": [
    {
      "where": {
        "selector_matches": "a.featured-link"
      },
      "eagerness": "eager"
    }
  ]
}

5.3 Complex Condition Composition

The and, or, and not operators enable complex logic:

{
  "prefetch": [
    {
      "where": {
        "or": [
          { "href_matches": "/products/*" },
          {
            "and": [
              { "href_matches": "/blog/*" },
              { "selector_matches": "a[data-popular='true']" }
            ]
          }
        ]
      },
      "eagerness": "moderate"
    }
  ]
}

5.4 CSS Class Toggle Pattern

A very useful pattern: declare rules based on classes, then use JavaScript to add/remove classes to control speculation dynamically:

// Speculation rule
{
  "prerender": [{
    "where": { "selector_matches": ".should-prerender" }
  }]
}

// JavaScript — add class when user signals intent to navigate
document.querySelector('a.cta-button')
  .classList.add('should-prerender');

6. Eagerness Levels: Controlling Speculation Timing

Eagerness determines when the browser starts speculation. It's the mechanism for balancing speed against resource cost.

Level Desktop Mobile Use Case
immediate As soon as rules are parsed Immediately Fixed URLs, high probability (next wizard step)
eager 10ms hover 50ms after link enters viewport Lightweight sites with many viewport links
moderate 200ms hover or pointerdown 500ms after scroll stops Best balance — recommended default
conservative Only on pointerdown/touchstart Only on pointerdown/touchstart Heavy sites, minimize resource waste
graph TD
    A[Eagerness Level] --> B[immediate]
    A --> C[eager]
    A --> D[moderate]
    A --> E[conservative]

    B --> B1[Limit: 50 prefetch / 10 prerender]
    C --> C1[Limit: 2 concurrent - FIFO]
    D --> D1[Limit: 2 concurrent - FIFO]
    E --> E1[Limit: 2 concurrent - FIFO]

    B1 --> F[Highest speed
Highest cost] E1 --> G[Lowest speed
Lowest cost] style A fill:#e94560,stroke:#fff,color:#fff style B fill:#2c3e50,stroke:#fff,color:#fff style C fill:#2c3e50,stroke:#fff,color:#fff style D fill:#4CAF50,stroke:#fff,color:#fff style E fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style F fill:#ff9800,stroke:#fff,color:#fff style G fill:#4CAF50,stroke:#fff,color:#fff
Eagerness levels: balancing speed vs resource cost

Important note

By default, URL List rules use immediate eagerness, while Document rules use conservative. In most cases, you should explicitly set moderate for Document rules — it's the best balance between effectiveness and resource efficiency.

7. Delivery Methods

7.1 Inline HTML — Simplest

Embed directly in your HTML:

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

7.2 HTTP Header — CDN-Friendly

Deliver speculation rules via HTTP header, ideal for CDN or reverse proxy setups:

Speculation-Rules: "/speculationrules.json"

The speculationrules.json file needs these headers:

Content-Type: application/speculationrules+json
Access-Control-Allow-Origin: *

This approach works great with Cloudflare Workers or Nginx — inject the header without modifying application code:

// Cloudflare Worker — inject Speculation-Rules header
export default {
  async fetch(request, env) {
    const response = await fetch(request);
    const newResponse = new Response(response.body, response);
    newResponse.headers.set(
      'Speculation-Rules',
      '"/speculationrules.json"'
    );
    return newResponse;
  }
};

7.3 Dynamic JavaScript — Most Flexible

Inject rules via JavaScript based on context:

function addSpeculationRules(urls) {
  if (!HTMLScriptElement.supports?.('speculationrules')) {
    return; // Browser doesn't support it
  }

  const script = document.createElement('script');
  script.type = 'speculationrules';
  script.textContent = JSON.stringify({
    prerender: [{
      urls: urls,
      eagerness: 'moderate'
    }]
  });
  document.head.appendChild(script);
}

// Usage: prerender popular pages based on analytics data
addSpeculationRules(['/hot-products', '/promotions']);

8. Handling Analytics and Side Effects

When a page is prerendered, JavaScript on that page runs immediately — including tracking scripts. Without proper handling, you'll double-count pageviews for prerendered pages that users never actually visit.

8.1 Detecting Prerender State

// Check if page is currently prerendering
if (document.prerendering) {
  console.log('Page is prerendering — not yet active');
}

// Check if page was activated from prerender
const navEntry = performance.getEntriesByType('navigation')[0];
if (navEntry?.activationStart > 0) {
  console.log('This page was prerendered before activation');
}

8.2 Deferring Analytics Until Activation

// Standard pattern: wait for activation before tracking
function trackPageView() {
  if (document.prerendering) {
    document.addEventListener('prerenderingchange', () => {
      sendAnalytics('pageview', window.location.href);
    }, { once: true });
  } else {
    sendAnalytics('pageview', window.location.href);
  }
}

trackPageView();

Good news

Google Analytics (GA4) and Google Tag Manager already detect and defer tracking during prerendering automatically. If you use GA4, no additional code is needed. However, custom-built or third-party analytics systems will need manual handling as shown above.

8.3 Cross-Tab State Synchronization

When users change state on the current page (e.g., adding to cart), prerendered pages may show stale data. Solution: use the BroadcastChannel API:

// Origin page — broadcast on state changes
const channel = new BroadcastChannel('app-state');
function addToCart(item) {
  cart.push(item);
  channel.postMessage({ type: 'cart-update', cart: cart });
}

// Prerendered page — listen and update
const channel = new BroadcastChannel('app-state');
channel.onmessage = (event) => {
  if (event.data.type === 'cart-update') {
    updateCartUI(event.data.cart);
  }
};

9. Debugging with Chrome DevTools

Chrome DevTools provides dedicated tools for debugging Speculation Rules:

Application Panel → Speculative Loads:

  • Speculation Rules: Shows all parsed rules, their status (active/cancelled), and cancellation reasons
  • Preloads: Lists URLs being prefetched/prerendered, loading progress, and activation status

Speculation states:

State Meaning
Not triggered Rule parsed but eagerness condition not yet met
Pending Waiting for system resources or an available slot
Running Currently prefetching/prerendering
Ready Complete, ready for activation
Success Successfully activated — user navigated to this page
Failure Cancelled for technical reasons (see details in DevTools)

10. Limitations and Constraints

The Speculation Rules API is not a silver bullet. Understanding its limitations helps you deploy it effectively.

10.1 Quantity Limits

Eagerness Max Prefetch Max Prerender Mechanism
immediate / eager 50 10 Fixed — excess speculations are ignored
moderate / conservative 2 2 FIFO — new speculations replace the oldest

10.2 Blocked Scenarios

  • Save-Data mode: Browser disables speculation when the user enables data saving
  • Energy Saver: Low battery + energy saver mode → no prerendering
  • Limited memory: Chrome automatically cancels speculations when RAM is low
  • User settings: "Preload pages" option disabled in Chrome settings
  • Cross-origin prerender: Not allowed by default (cross-origin prefetch is OK)

10.3 Deferred APIs During Prerender

Some "intrusive" APIs are deferred until the page is activated:

  • window.alert(), window.confirm(), window.prompt()
  • window.print()
  • Geolocation API
  • Notifications API
  • Fullscreen request
  • Payment Request API

New feature: Prerender Until Script (2026)

Chrome is experimenting with prerender_until_script — a middle ground between prefetch and prerender. The browser loads HTML + subresources and starts rendering, but stops before executing any scripts. This captures rendering benefits without JavaScript side effects. The origin trial has been running since January 2026.

11. Impact on Core Web Vitals

This is the real reason the Speculation Rules API is a game-changer:

LCP ~0Largest Contentful Paint near-instant with prerender
CLS ↓Layout shifts happen in the hidden tab
INP ↓Resources already loaded, faster interactions
TTFB ~0No network request needed on navigation

Metric-by-metric analysis:

  • LCP (Largest Contentful Paint): With prerender, the page is fully rendered in the hidden tab. On activation, LCP is measured from activationStart, not navigationStart. If the LCP element rendered before activation → LCP ≈ 0ms.
  • CLS (Cumulative Layout Shift): Layout shifts during prerendering don't count toward CLS (the user hasn't seen the page yet). Only shifts after activation are measured.
  • INP (Interaction to Next Paint): Resources are loaded, DOM is built → first interactions respond significantly faster.
  • TTFB: Effectively zero since there's no network round-trip — the page is already in memory.

Measurement note

In CrUX (Chrome User Experience Report), navigations from prerender are tracked separately. You can segment the data to compare performance between pages with and without speculation rules. Use performance.getEntriesByType('navigation')[0].activationStart > 0 to detect prerendered pages in your RUM setup.

12. Production Deployment Strategy

Speculation Rules deployment should be progressive, from safe to aggressive:

Step 1: Broad Prefetch

Start with prefetch + moderate eagerness for all internal links. Low cost, near-zero risk, with 50-80% navigation speed improvement. This is the safest step with the highest ROI.

{
  "prefetch": [{
    "where": { "href_matches": "/*" },
    "eagerness": "moderate"
  }]
}
Step 2: Selective Prerender

Add prerender + conservative for high-probability pages: main landing pages, next steps in flows, prominent CTA links. Monitor activation rates to evaluate effectiveness.

{
  "prerender": [{
    "where": {
      "selector_matches": "a.primary-cta, a.next-step"
    },
    "eagerness": "conservative"
  }]
}
Step 3: Expand Prerender

Increase eagerness to moderate for prerender. Add document rules with exclusion conditions for heavy pages or those with side effects. Combine broad prefetch with selective prerender.

Step 4: Data-Driven Optimization

Use analytics data to build dynamic speculation rules. Inject URL lists based on top exit pages, A/B test eagerness levels, and monitor waste ratio (prerenders that never get navigated to).

Complete implementation example for a blog/news site

<script type="speculationrules">
{
  "prefetch": [
    {
      "where": {
        "and": [
          { "href_matches": "/*" },
          { "not": { "href_matches": "/login" } },
          { "not": { "href_matches": "/register" } },
          { "not": { "href_matches": "/admin/*" } },
          { "not": { "href_matches": "/api/*" } },
          { "not": { "selector_matches": "[download]" } }
        ]
      },
      "eagerness": "moderate"
    }
  ],
  "prerender": [
    {
      "where": {
        "selector_matches": "article a:first-of-type"
      },
      "eagerness": "moderate"
    }
  ]
}
</script>

This ruleset will: (1) prefetch all internal links except auth/admin/API endpoints, and (2) prerender the first link in each article — typically a related or latest post that users are very likely to click.

13. Browser Support

Browser Prefetch Prerender Document Rules Notes
Chrome 109+ 109+ 121+ Full support, tags from 136+
Edge 109+ 109+ 121+ Chromium-based, matches Chrome
Opera 95+ 95+ 107+ Chromium-based
Firefox No No No Under consideration, no roadmap yet
Safari Experimental No No Behind experimental flag on latest builds

Progressive Enhancement

The Speculation Rules API works as progressive enhancement — browsers that don't support it simply ignore the <script type="speculationrules"> tag. No breaking changes, no impact on older browsers. You can feature-detect with: HTMLScriptElement.supports?.('speculationrules')

14. Conclusion

The Speculation Rules API represents a major leap in web performance — not by shaving off a few dozen milliseconds, but by eliminating navigation wait time entirely. With near-zero implementation cost (just add a JSON snippet), the payoff is near-instant LCP for most navigations.

The best strategy: start with prefetch moderate for all internal links, then gradually add prerender for high-probability pages. Monitor through Chrome DevTools and CrUX data to fine-tune. This is one of those rare "quick wins" in web performance — low effort, high impact.

References:
Chrome Developers — Prerender pages for instant navigations
Chrome Developers — Implementing speculation rules for complex sites
MDN Web Docs — Speculation Rules API
DebugBear — Blazing Fast Websites with Speculation Rules
Chrome Developers — Prerender Until Script Origin Trial