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.
Related Engineering Labs
Calculator
FinOps Estimator
Estimate CI/CD compute cost from build volume, duration, retry overhead, and a user-supplied blended hourly rate, then compare target-duration scenarios.
Calculator
JWT Decoder
Strictly decode compact signed JSON Web Tokens and inspect headers, claims, signature presence, and advisory time checks locally in your browser.
Related articles
Software Architecture
Safe API Order Creation with Idempotency Keys
How to design, canary-deploy, evidence-check and safely roll back a bounded API architecture change without treating any single layer as trustworthy on its own.
Software Architecture
Circuit-Breaker Isolation Boundaries for a Bounded API Software Architecture Workflow
How to add a circuit-breaker and bulkhead isolation boundary around one API dependency, with staged shadow-to-enforcing rollout, explicit validation and a prepared rollback path.
Software Architecture
Rolling Out a New API Version Without Breaking Existing Consumers
A bounded, evidence-led method for rolling out a new API version behind an existing gateway using weighted traffic splitting, explicit validation gates and a rehearsed rollback path.
Software Architecture
Running a 5% API Canary with Health Gates
A bounded, evidence-led workflow for routing a small percentage of API traffic to a new deployment, validating it against explicit thresholds, and rolling it back deterministically if it fails.
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.
Comments
Add a thoughtful note on Speculation Rules API: Architecting Safe Prerendering. Comments are checked for spam and held for moderation before appearing.