SiteTidy
HomeGuidesSpeculation Rules API: Faster Navigation Without Accidental Double Loads
Performance

Speculation Rules API: Faster Navigation Without Accidental Double Loads

A practical guide to using the Speculation Rules API for prefetch and prerender, including eagerness, URL matching, analytics, authentication, side effects, caching and safe rollout.

Published on September 1, 2026

There is a very appealing version of page-speed work where the browser simply starts loading the next page before the visitor clicks it.

Sometimes that is exactly the right thing to do.

It is also easy to turn that idea into wasted bandwidth, misleading analytics, unnecessary origin traffic, or a page that performs work before the user has actually decided to visit it.

The Speculation Rules API gives the browser a structured way to prefetch or prerender likely future navigations. Compared with sprinkling <link rel="prefetch"> around a site, it gives you much better control over which documents are candidates and how eagerly the browser should speculate.

The part worth understanding is not the JSON syntax. It is deciding what you are prepared to execute before a real navigation happens.

Prefetch and prerender are not the same optimisation

The API supports two useful kinds of speculation for documents.

A prefetch asks the browser to fetch a likely next document so its response may already be available when the user navigates.

A prerender goes much further. The browser can load the document in a hidden context and perform much of the work required to make it ready for activation.

A small rule set can look like this:

<script type="speculationrules">
{
  "prefetch": [
    {
      "urls": ["/pricing", "/docs/getting-started"]
    }
  ]
}
</script>

Or, for a page you have very high confidence will be visited next:

<script type="speculationrules">
{
  "prerender": [
    {
      "urls": ["/checkout/review"]
    }
  ]
}
</script>

The tempting approach is to think of prerender as “better prefetch”. I would not treat it that way.

Prefetch is primarily a network bet. Prerender is a much larger behavioural bet because the destination document may start running code before the user visibly arrives there. The performance upside is larger, but so is the amount of application behaviour you need to understand.

Start with the navigation you are trying to improve

Before writing a rule, identify an actual transition.

For example:

/product/green-widget

/cart

/checkout

If most product-page visitors never add the item to their cart, eagerly prerendering /cart from every product page is poor economics. You spend CPU, network and server resources on work that often gets thrown away.

But once somebody has added an item and is looking at the cart, /checkout may be a much stronger candidate.

This is why I would design speculation around navigation probability, not around a list of pages that happen to be important to the business.

High-value pages are not automatically high-confidence next pages.

eagerness is where the useful control lives

Speculation rules can include an eagerness value. Current implementations distinguish levels such as immediate, eager, moderate and conservative, allowing the browser to delay speculation until there is stronger evidence that a link may be followed.

For example:

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

This is much more sensible than blindly fetching every guide linked from a page as soon as the document loads.

The exact heuristics are browser-controlled, which is intentional. Your rule expresses that a URL is a valid speculation candidate and how aggressive you want the browser to be; it does not give you a millisecond-by-millisecond loading scheduler.

For a large content site I would normally begin with conservative or moderate speculation on internal links, measure it, and only move specific transitions toward eager or immediate behaviour when the evidence supports doing so.

List rules versus document rules

There are two useful ways to describe candidates.

A list rule names URLs explicitly:

{
  "prefetch": [
    {
      "urls": ["/pricing", "/contact"]
    }
  ]
}

That works well when the next step is known and finite.

A document rule selects links in the current page. This is where the API becomes much more practical for documentation, ecommerce and editorial sites.

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

Now you can define a broad class of useful internal navigations while retaining an escape hatch:

<a href="/guides/csp" class="no-speculation">CSP guide</a>

I prefer this model when a site has hundreds or thousands of pages. Maintaining an explicit generated URL list merely recreates a sitemap inside your HTML.

A rule that matches all same-site links can look elegant:

{
  "where": {
    "href_matches": "/*"
  }
}

It can also include routes you never intended to touch speculatively:

  • sign-out endpoints;
  • account actions;
  • cart mutations implemented badly as GET requests;
  • links that create export jobs;
  • huge downloadable documents;
  • pages with expensive server-side rendering;
  • one-time links;
  • URLs whose response varies heavily by authentication or session state.

Some of those are application bugs in their own right. A GET request should not be used for destructive state changes. Speculative loading simply has a habit of finding assumptions that were previously hidden.

The safer pattern is to start with a positive allowlist of route families whose behaviour you understand.

Prerender changes the meaning of “page load”

This is the part I would review most carefully before enabling prerender on an application with analytics or side effects.

A prerendered page can exist before the visitor sees it. Code may begin executing while the document is hidden. When the user eventually navigates, that prerendered document can be activated rather than loaded from scratch.

That means this mental model is no longer reliable:

JavaScript started = visitor viewed the page

Those events can happen at different times.

The Page Visibility API and prerender-related document state are therefore not obscure implementation details. They can affect whether you should initialise expensive UI, start timers, record impressions or perform other user-visible work.

MDN’s prerendering documentation and Chrome’s prerender guidance are worth reading alongside your analytics implementation rather than treating speculation as a CSS-and-headers optimisation.

Analytics needs activation-aware thinking

Imagine this sequence:

10:00:00  Browser begins prerendering /pricing
10:00:04  Analytics library initialises
10:00:12  User actually clicks Pricing
10:00:12  Prerendered page activates

If your analytics records a page view at 10:00:04, you have counted an apparent visit eight seconds before the visitor viewed the page. If the user never clicks, you may count a visit that never happened at all.

Do not assume your analytics provider handles this correctly merely because it is widely used. Check its current documentation and test the actual network requests.

For custom analytics, the safer principle is straightforward: record visibility-dependent events when the document is actually presented, not merely when its JavaScript environment exists.

This matters beyond page views. Ad impressions, experiment exposure, “article read” events and conversion funnel steps can all become misleading if they fire during speculative execution.

If you are already tightening your event model, SiteTidy’s guide to tracking events with Google Tag Manager is a useful adjacent read.

Authentication makes speculation more interesting

A speculative request is still a real request.

Your server may therefore see normal-looking traffic for a page the visitor has not yet opened. Whether credentials are sent, whether the response can be reused, and whether the browser is allowed to speculate depend on the type of speculation, origin relationship, browser policy and response headers involved.

Do not design authentication logic around an assumption that “a request only happens after a click”. That assumption was already shaky because of crawlers, previews and browser features; speculation makes it explicitly unsafe.

I would be especially cautious with routes that:

  • rotate tokens during ordinary page loads;
  • consume one-time state;
  • update a last_seen timestamp as a business event;
  • trigger server-side jobs just because HTML was requested;
  • depend on the first request having a visible user interaction behind it.

The fix is generally not “block the Speculation Rules API”. The fix is to separate rendering a safe document from actions that genuinely require user intent.

Your server needs to recognise speculative traffic

Browsers can attach request metadata that helps servers identify speculation-related navigations. The relevant behaviour is documented in the Speculation Rules specification and browser documentation.

Use that information for observability before you use it for clever behaviour.

For example, I would want logs or metrics that let me answer:

How many speculative requests did we receive?
How many were eventually activated?
Which routes generated most unused work?
Did origin CPU or cache churn increase?
Did the target navigation actually get faster?

Without those answers, a faster-looking lab demo can quietly become a more expensive production system.

Do not automatically return a radically different representation to every speculative request unless you have tested activation and caching semantics carefully. Varying responses creates another set of cache keys and failure modes to reason about.

Cache behaviour can make or break the result

Prefetch is most valuable when the work it performs can actually be reused by the subsequent navigation.

That brings ordinary HTTP caching back into the picture.

If every document response is effectively uncacheable, generated uniquely, or invalidated immediately, you may still gain something from browser-managed speculation, but you should not assume the benefit will match a simple static-page demo.

Review:

  • Cache-Control;
  • Vary;
  • authentication-dependent responses;
  • CDN behaviour;
  • redirects;
  • cookies set by the destination;
  • whether the eventual navigation receives the response you expected.

A browser feature cannot repair incoherent caching policy.

If you are debugging response behaviour generally, the SiteTidy audit is useful for checking the public response and related headers before you start blaming the navigation layer.

Be careful with third-party scripts

Prerendering a document can cause code on that document to initialise before activation. That can include third-party code you do not control.

A chat widget, consent platform, recommendation engine or marketing script may have assumptions about visibility that your application does not.

This is one reason I would introduce prefetch before prerender on a mature site. Prefetch tests whether speculative network work provides a worthwhile navigation improvement with a smaller behavioural surface area. Once that is understood, prerender can be added to the handful of transitions where near-instant activation is valuable enough to justify the extra scrutiny.

That order is less exciting than turning on prerender everywhere. It is also much easier to debug.

A practical first policy

For a documentation or editorial site with many safe internal pages, I would start somewhere around this level of ambition:

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

Then measure whether navigation latency improves and whether unused prefetch traffic is acceptable.

For a very predictable funnel, I might add a narrowly targeted prerender rule later:

<script type="speculationrules">
{
  "prerender": [
    {
      "urls": ["/checkout/review"],
      "eagerness": "eager"
    }
  ]
}
</script>

I would not begin with a site-wide immediate prerender rule. The difference between “technically supported” and “sensible in production” is substantial here.

Progressive enhancement is the right compatibility model

The Speculation Rules API is an optimisation. Your navigation must still work perfectly when the browser does not support it, chooses not to speculate, cancels speculation, or discards a prerender.

That is a healthy constraint.

Keep normal <a href> navigation as the source of truth. Do not build application correctness around a speculative document being available. The browser is deliberately allowed to make resource-management decisions you do not control.

You can feature-detect the script type from JavaScript when you genuinely need conditional setup, but for many static rule sets it is reasonable simply to let unsupported browsers ignore the speculation rules and navigate normally.

For current browser support details, check MDN’s compatibility data rather than copying a support table into application documentation and allowing it to go stale.

Test activation, not just loading

A convincing test should cover both branches:

  1. the browser speculates and the user follows the link;
  2. the browser speculates and the user does not follow the link.

The second case catches much of the waste and accidental behaviour.

In development tools, inspect the browser’s speculation/prerender tooling where available, network requests, server logs and application analytics. Test authenticated and anonymous sessions separately if your responses differ.

I would also deliberately test pages that contain:

analytics
consent tooling
third-party embeds
WebSocket/SSE setup
authentication checks
expensive API calls
client-side redirects
session mutations

You are looking for code whose real trigger was accidentally “the document started” when what you meant was “the visitor is now looking at this document”.

Measure the right outcome

A speculation rule is successful when it improves a meaningful navigation without unacceptable cost.

Do not stop at “the prefetched request appeared in DevTools”. Measure user-facing navigation performance and watch the server side too.

For page experience work, this sits alongside rather than replaces the fundamentals covered in the Core Web Vitals guide. Speculation can make a future navigation feel dramatically faster; it does not excuse a heavy destination page, unstable layout or slow interaction once the user is there.

A useful rollout dashboard would show at least:

Signal What you are trying to learn
Candidate navigations How often the rule could apply
Speculations started How aggressive the browser actually was
Activations / useful hits How much speculative work paid off
Unused work Bandwidth and origin cost that produced no visit
Navigation latency Whether users received the intended benefit
Error/side-effect rate Whether speculative execution exposed broken assumptions

The ratio matters more than the raw count. Ten thousand prefetches sound impressive until only a few hundred are ever used.

The safest rule is the one you can explain

The Speculation Rules API is one of the more interesting performance tools available to ordinary websites because it lets the browser move work ahead of the click without requiring a client-side router.

That does not mean every link should become speculative.

I would ship it in this order:

  1. identify a genuinely slow and predictable navigation;
  2. start with a narrow prefetch rule;
  3. observe speculative traffic and unused work;
  4. verify analytics and server-side behaviour;
  5. move only high-confidence transitions to prerender;
  6. expand matching rules after the economics are understood.

The browser can make navigation feel almost instantaneous when you give it useful information. Your job is to make sure the information represents a sensible bet rather than enthusiasm disguised as optimisation.

Primary references

The behaviour of this API is still evolving, so implementation decisions should be checked against current primary documentation rather than old blog posts:

Ready to audit your site?

Put this guide into practice immediately using our free tools.

Browse 180+ Free Tools