Skip to main content
Systems Engineering

Speculation Rules API: Architecting Safe Prerendering

How document rules, score-based heuristics and No-Vary-Search headers let the Speculation Rules API prerender navigations without wasting origin compute.

Speculation Rules API: Architecting Safe Prerendering

In this guide

Share

A cold client-side navigation on a content-heavy SPA typically costs 200-800ms between click and first paint, even with a warm HTTP cache: route resolution, hydration, and data fetching all execute serially on the main thread. Prefetching the JavaScript bundle helps marginally, but it does nothing for render cost. The speculation rules api closes this gap by letting the browser fetch, render, and in some cases fully execute a destination page before the user commits to the click, so the eventual navigation becomes a document swap rather than a cold load. This article covers the architecture of that mechanism, how to roll it out without corrupting analytics or leaking session state, and where it breaks under production load.

#The Cost of Client-Side Navigation Latency

Traditional mitigation for navigation latency falls into two camps: <link rel="prefetch">, which warms the HTTP cache but leaves parsing, layout, and hydration on the critical path; and full route-level code-splitting with eager imports, which shifts cost earlier but doesn’t touch render or data-fetch latency. Neither addresses the actual bottleneck on JavaScript-heavy micro-frontend shells: the browser still has to construct a render tree from zero at click-time.

The speculation rules api — standardised by the WICG and shipping in Chromium-based browsers — introduces a declarative JSON ruleset that tells the browser which URLs are candidates for background prefetch or full prerendering. A prerendered page is rendered in a hidden, inactive browsing context with restricted capabilities, then activated instantly on navigation. Correctly implemented, this converts a 600ms navigation into a sub-50ms tab swap. Incorrectly implemented, it duplicates analytics events, triggers unintended side effects on GET endpoints, and burns origin compute on speculative traffic that never converts.

#Architectural Breakdown: How the Speculation Rules API Works

The specification defines two distinct behaviours under one API surface, and conflating them is the most common architectural mistake teams make when adopting the speculation rules api.

#
Prefetch vs Prerender Semantics

Prefetch rules fetch the response body and stash it in the HTTP cache, tagged for the navigation. No DOM construction, no script execution. Prerender rules go further: they load the destination in a separate, throttled renderer process, run its JavaScript, and construct a full DOM — all before the user clicks. The trade-off is resource cost against latency reduction.

StrategyRendering costLatency reductionSide-effect riskBest fit
rel=prefetch (link)NoneLow (network only)LowStatic assets, fonts
Speculation Rules: prefetchNoneMediumLow-MediumAPI-driven SPA routes
Speculation Rules: prerenderFull DOM + JS executionHigh (near-instant)High if unmitigatedHigh-confidence next-page navigation
Service Worker cache warmNoneMediumMedium (stale data)Offline-first PWAs

Because prerendering executes real JavaScript, any code that fires on load — analytics beacons, A/B test bucketing, cart mutation calls — will fire during speculation, not just on actual navigation. The API mitigates this via the document.prerendering flag and the prerenderingchange event, but only if engineering teams explicitly gate side-effecting code behind them.

speculation rules api

#Implementation Logic: Rolling Out the Speculation Rules API

A production rollout of the speculation rules api follows four stages: rule authoring, eligibility scoring, activation gating, and observability. Skipping the third stage is where most incidents originate.

#
1. Static and Dynamic Rule Authoring

Rules can be inlined as a <script type="speculationrules"> block or served via HTTP header for CDN-level control without touching the HTML template:

1{
2  "prerender": [
3    {
4      "source": "document",
5      "where": {
6        "and": [
7          { "href_matches": "/product/*" },
8          { "not": { "href_matches": "/product/*/checkout" } }
9        ]
10      },
11      "eagerness": "moderate"
12    }
13  ],
14  "prefetch": [
15    {
16      "source": "list",
17      "urls": ["/api/session-warm", "/cart"],
18      "requires": ["anonymous-client-ip-when-cross-site"]
19    }
20  ]
21}

The eagerness field controls the trigger threshold: immediate fires on pointerdown, moderate waits for sustained hover intent (roughly 200ms), and conservative requires near-certain intent signals. For most catalogue-style navigation, moderate gives the best balance between hit rate and wasted speculative renders.

#
2. Serving Rules via HTTP Header for CDN Control

Rather than baking rules into every HTML response, serve them from the edge so the ruleset can be updated without a redeploy:

1location / {
2    add_header Speculation-Rules "/speculation-rules.json";
3    add_header No-Vary-Search "params=(utm_source utm_campaign)";
4}

The No-Vary-Search header is essential in any architecture using UTM parameters or session tracking query strings — without it, the browser treats /product/42?utm_source=x and /product/42 as distinct cache keys, and every prerendered speculative fetch is wasted the moment a tracking parameter changes.

#
3. Gating Side Effects During Speculation

Any code with observable side effects — analytics, feature-flag evaluation with server mutation, cart pings — must check the prerendering state before firing:

Speculation Rules API: Architecting Safe Prerendering architecture diagram 2
1function trackPageView(pageId) {
2  if (document.prerendering) {
3    document.addEventListener('prerenderingchange', () => {
4      trackPageView(pageId);
5    }, { once: true });
6    return;
7  }
8  analytics.track('page_view', { pageId, activatedFrom: 'speculation' });
9}

This defers the event until the page is actually activated, preventing phantom page-view inflation in downstream analytics pipelines — a failure mode that has silently corrupted conversion funnels in more than one production rollout.

Rendering diagram...

#Failure Modes and Edge Cases

The speculation rules api degrades gracefully by design — unsupported browsers simply ignore the ruleset — but several failure modes are specific to the speculative execution

model itself, not to browser support gaps.

  • POST and non-idempotent GET routes: the specification explicitly forbids prerendering pages reachable only via forms, and any GET endpoint with mutation side effects (add-to-cart-via-link patterns) will silently execute during speculation, corrupting state before the user has committed to anything.
  • Memory pressure eviction: prerendered renderers consume full process memory. Under constrained devices, Chromium evicts speculative renders opportunistically, meaning the “instant” navigation degrades back to a cold load with no error surfaced to the application — monitoring must treat this as expected variance, not a bug.
  • Cross-origin restriction: prerendering across origins requires the destination to opt in via a matching Supports-Loading-Mode: credentialed-prerender response header; without it, cross-origin speculative rules silently downgrade to no-op.
  • Service worker interception conflicts: a fetch event handler that assumes an active, foregrounded document will misbehave in the prerendering context, since self.clients reports the speculative client with a distinct visibility state.
  • Duplicate mutation on activation: code that binds mutation logic to DOMContentLoaded rather than gating on document.prerendering will run twice — once during speculative render, once (if re-triggered) on activation — unless idempotency is enforced at the request layer.

#Scaling and Security Trade-offs

Rolling this out fleet-wide inside a micro-frontend shell interacts directly with existing architectural patterns around edge caching, CSP, and origin capacity planning. The trade-offs are not symmetrical — prerendering shifts cost from user-perceived latency to origin compute and privacy surface area.

  • Origin load: a naive eagerness: immediate rule across a high-traffic catalogue can 3-5x origin request volume from speculative renders that never convert; start with moderate and measure activation-to-speculation ratio before widening scope.
  • Cache key correctness: without precise No-Vary-Search configuration, CDN cache hit ratios for speculative traffic collapse, and every hover event becomes a full origin round trip.
  • Referrer and history leakage: prerendered documents can read document.referrer and populate browser history speculatively; sensitive destinations (account settings, payment flows) should be explicitly excluded via not clauses in the ruleset rather than relying on default restrictions.
  • CSP interaction: a strict Content-Security-Policy with prefetch-src or default-src directives that omit speculative fetch destinations will block the ruleset outright with no console warning surfaced to most developers unaware of this interaction.
  • Third-party script cost: tag-manager scripts that fire network calls on load will execute during speculative prerendering unless explicitly gated, multiplying third-party vendor billing for impressions that never resulted in an actual page view.
  • Bot and crawler exposure: automated crawlers that programmatically hover or rapidly traverse links can trigger disproportionate prerender volume; rate-limiting speculative fetch endpoints separately from standard navigation traffic is advisable at the edge layer.

The specification text itself, maintained by the WICG, is the authoritative reference for eagerness thresholds and header semantics: WICG Speculation Rules specification. Teams adopting the speculation rules api at scale should treat the ruleset as a distinct deployable artifact with its own rollback path, versioned independently from the application bundle, since a malformed rule fails silently rather than throwing a build error — the only signal is a navigation that quietly stops feeling instant.

Evidence trail

Sources and verification

Primary documentation and external technical references used in this article.

  1. 01WICG Speculation Rules specificationwicg.github.io
Marcus Thorne

Marcus Thorne

Systems Engineering Editor

Marcus Thorne is a pragmatic software architect focused on highly concurrent, distributed transactional systems.

Published Last changed
View Profile
Reader Interaction

Comments

Add a thoughtful note on Speculation Rules API: Architecting Safe Prerendering. Comments are checked for spam and held for moderation before appearing.

Loading comments...
Comment submission is disabled until Cloudflare Turnstile keys are configured.

Learn More About KBY

Was this useful?

Engineering insights, direct to you.

Receive the latest Systems Engineering tutorials, production guides, Engineering Labs and operational best practices.