Speculation Rules API — Achieve Near-Instant Page Navigations
Posted on: 4/24/2026 9:14:53 PM
Table of contents
- 1. The Problem: Why Web Navigation Is Still Slow
- 2. What Is the Speculation Rules API?
- 3. Prefetch vs Prerender: Two Levels of Optimization
- 4. JSON Syntax and Implementation
- 5. Document Rules — Smart Conditional Prerendering
- 6. Eagerness Levels: Controlling Speculation Timing
- 7. Delivery Methods
- 8. Handling Analytics and Side Effects
- 9. Debugging with Chrome DevTools
- 10. Limitations and Constraints
- 11. Impact on Core Web Vitals
- 12. Production Deployment Strategy
- 13. Browser Support
- 14. Conclusion
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.
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
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
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:
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, notnavigationStart. 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:
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"
}]
}
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"
}]
}
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.
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
YARP — Building API Gateway for Microservices on .NET 10
Cloudflare Dynamic Workers — Stateful Serverless for the AI Agent Era
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.