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.

In this guide
Table of Contents
Table of contents
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.
| Strategy | Rendering cost | Latency reduction | Side-effect risk | Best fit |
|---|---|---|---|---|
| rel=prefetch (link) | None | Low (network only) | Low | Static assets, fonts |
| Speculation Rules: prefetch | None | Medium | Low-Medium | API-driven SPA routes |
| Speculation Rules: prerender | Full DOM + JS execution | High (near-instant) | High if unmitigated | High-confidence next-page navigation |
| Service Worker cache warm | None | Medium | Medium (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.

#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:

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
- 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-prerenderresponse 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.clientsreports the speculative client with a distinct visibility state. - Duplicate mutation on activation: code that binds mutation logic to
DOMContentLoadedrather than gating ondocument.prerenderingwill 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: immediaterule across a high-traffic catalogue can 3-5x origin request volume from speculative renders that never convert; start withmoderateand measure activation-to-speculation ratio before widening scope. - Cache key correctness: without precise
No-Vary-Searchconfiguration, 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.referrerand populate browser history speculatively; sensitive destinations (account settings, payment flows) should be explicitly excluded vianotclauses in the ruleset rather than relying on default restrictions. - CSP interaction: a strict
Content-Security-Policywithprefetch-srcordefault-srcdirectives 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.
Comments
Add a thoughtful note on Speculation Rules API: Architecting Safe Prerendering. Comments are checked for spam and held for moderation before appearing.
Related Engineering Labs
Related articles
Software Architecture
Designing Compensating Sagas for Microservices
How orchestrator state machines, compensating handlers and idempotency ledgers make the saga orchestration pattern safe for distributed writes.
Software Architecture
A Bounded Recovery Path for API-Driven Software Architecture Changes
How to design, validate and recover one bounded API architecture change with explicit evidence, bounded failure containment and a fixed rollback path.
Software Architecture
Building a Recoverable API Workflow for Software Architecture Reliability
A bounded, evidence-led approach to introducing and safely recovering a single API-mediated architectural change, using a routing boundary as the containment mechanism.
Software Architecture
Building a Failure-Aware API Workflow for Software Architecture
A bounded, failure-aware pattern for implementing a software architecture workflow on an API, with explicit validation stages, failure containment and a defined rollback ladder.
Learn More About KBY
About KBY
Learn about our mission, editorial standards, and commitment to trusted engineering knowledge.
Why Trust KBY
Explore the processes and policies that ensure our publications are accurate, useful, and responsible.
Newsletter
Get our latest editorial publications, research and practical insights sent directly to your inbox.
Was this useful?
Engineering insights, direct to you.
Receive the latest Systems Engineering tutorials, production guides, Engineering Labs and operational best practices.