Speculation Rules API w 2026: prerender, prefetch i strategie eagerness dla natychmiastowej nawigacji

Praktyczny opis Speculation Rules API w Chromium 2026: prerender vs prefetch, cztery poziomy eagerness, No-Vary-Search, debugowanie w DevTools i wpływ na Core Web Vitals.

Zaktualizowano: 21 sierpnia 2026

Speculation Rules API to natywny mechanizm przeglądarki, który pozwala witrynie deklaratywnie wskazać, które kolejne strony powinny zostać prefetchowane lub prerenderowane jeszcze zanim użytkownik w nie kliknie. Po aktywacji nawigacja jest praktycznie natychmiastowa (LCP często poniżej 100 ms). W 2026 API jest stabilne w Chromium od wersji 122, obsługuje reguły document, hint No-Vary-Search oraz cztery poziomy eagerness. W tym artykule pokazuję każdą konfigurację z gotowym kodem, cyframi z realnych pomiarów i pułapkami, których nauczyłem się bolesną drogą.

  • Prerender ≠ prefetch: prefetch pobiera odpowiedź HTML, prerender wykonuje pełny render (HTML + CSS + JS + obrazy) w ukrytym procesie.
  • Cztery poziomy eagerness (immediate, eager, moderate, conservative) pozwalają skalować agresywność od „prerender wszystko" do „tylko przy pointerdown".
  • Reguły typu document (Chrome 121+) pozwalają programowo wybrać linki wg selektora CSS zamiast statycznej listy URL-i.
  • No-Vary-Search mówi Chrome, że parametry query nie zmieniają dokumentu, więc ?utm_source=… nie unieważnia prerender-a.
  • Prerenderowana strona ma document.prerendering === true; aktywację obsługujesz przez prerenderingchange. Analityka musi to uwzględniać, inaczej duplikuje odsłony.
  • Firefox i Safari (WebKit) w 2026 nie implementują Speculation Rules. To progresywne wzmocnienie, nie fundament.

Czym jest Speculation Rules API?

Speculation Rules API to standard Web Incubator Community Group, który pozwala umieścić w dokumencie tag <script type="speculationrules"> z konfiguracją JSON opisującą, co przeglądarka może wstępnie pobrać lub w pełni wyrenderować. Zamiast dodawać setki tagów <link rel="prefetch"> i konkurować z resource hints, deklarujesz jedną politykę, a przeglądarka sama pilnuje ograniczeń pamięci, sieci i akumulatora.

W praktyce API rozwiązuje trzy problemy, z którymi walczyłem od lat. Po pierwsze, <link rel="prerender"> został usunięty w Chrome 63, bo zjadał RAM na urządzeniach mobilnych. Nowy prerender jest zarządzany: Chrome przyznaje maksymalnie dwie prerenderowane strony jednocześnie dla eagerness: immediate/eager oraz do dziesięciu dla moderate/conservative, i sam je porzuca, gdy urządzenie ma mało pamięci albo jest w trybie oszczędzania energii (Data Saver, Battery Saver).

Po drugie, dawne rel="prefetch" trafiało do cache HTTP, przez co nawet drobna różnica w URL (np. utm_source) unieważniała cache. Speculation Rules trzyma odpowiedź w dedykowanym „memory cache" żywotnym tylko dla bieżącej sesji, dzięki czemu ryzyko wycieku prywatności jest mniejsze, a nowy nagłówek No-Vary-Search radzi sobie z wariantami URL. Po trzecie: prerender wywołuje pełny cykl renderowania w niewidocznym procesie, więc gdy użytkownik faktycznie kliknie link, przeglądarka „aktywuje" gotowy dokument (swap), a Ty widzisz LCP zbliżony do zera i zerowe FCP.

Prerender vs prefetch: kiedy używać każdej strategii?

Największe nieporozumienie, jakie widzę w audytach: zespoły wrzucają wszędzie prerender, bo brzmi „mocniej". Tymczasem prerender jest 5–8× droższy pamięciowo od prefetch, bo uruchamia JS, layout, paint i ładuje wszystkie sub-resource'y. Poniżej tabela decyzyjna, którą powtarzam każdemu klientowi.

CechaPrefetchPrerender
Co robiPobiera HTML odpowiedzi do memory cachePełny render HTML + CSS + JS + obrazy
Koszt pamięciNiski (~50–200 KB / strona)Wysoki (~5–40 MB / strona)
Wpływ na LCP po kliknięciuPoprawia TTFB, LCP zależne od CSS/JSLCP ≈ 0 ms (dokument już wyrenderowany)
Limit współbieżny (Chrome 2026)~50 URL-i2 (immediate/eager), 10 (moderate/conservative)
Wykonuje JavaScript?NieTak, wraz z wszystkimi side-effectami
Idealny scenariuszListy produktów, wyniki wyszukiwania, artykułyPierwszy link w wynikach, „następny artykuł", checkout step 2

Reguła kciuka: prefetch dla wielu prawdopodobnych ścieżek, prerender dla jednej najbardziej prawdopodobnej. Na sklepie e-commerce zaczynam od prefetchu top 20 produktów listingu i prerender-a wyłącznie tego, na który użytkownik najedzie na dłużej niż 200 ms. Takie połączenie w moich pomiarach zmniejszało medianowy LCP kolejnej strony z 1,8 s do 220 ms bez zauważalnego wzrostu zużycia RAM na średnim Androidzie (Redmi Note 12).

Jeśli budujesz witrynę wielostronicową i chcesz połączyć prerender z płynnymi animacjami między dokumentami, sprawdź też mój przewodnik po Cross-document View Transitions dla e-commerce. Obie technologie zaprojektowano tak, by pracowały razem.

Poziomy eagerness: immediate, eager, moderate, conservative

Eagerness kontroluje, jak agresywnie Chrome wykonuje regułę. To najczęściej źle konfigurowany parametr, bo dokumentacja wielu frameworków po prostu ustawia immediate i zapomina o kosztach.

  • immediate: akcja odpala się natychmiast po sparsowaniu reguły. Używam wyłącznie dla ścieżek, w które użytkownik na pewno pójdzie (np. checkout krok 2 po dodaniu do koszyka).
  • eager: akcja odpala się natychmiast, ale przeglądarka może opóźnić lub porzucić w warunkach niskiego CPU albo małej ilości RAM. Dobre dla „following article" w blogu.
  • moderate: akcja odpala się, gdy użytkownik najedzie kursorem na link przez ~200 ms lub dotknie go palcem. Domyślne dla reguł typu document.
  • conservative: akcja odpala się dopiero przy pointerdown / touchstart. W praktyce daje ~80 ms przewagi nad zwykłym kliknięciem, subtelne, ale zauważalne.

Poniżej minimalny przykład reguły list z różnymi poziomami eagerness dla różnych URL-i:

<script type="speculationrules">
{
  "prerender": [
    {
      "urls": ["/checkout/summary"],
      "eagerness": "immediate"
    }
  ],
  "prefetch": [
    {
      "urls": ["/product/next", "/product/related-1", "/product/related-2"],
      "eagerness": "eager"
    }
  ]
}
</script>

W testach A/B na portalu newsowym (2,4 mln UU/miesiąc) zmiana immediate na moderate dla reguły document obniżyła transfer o 38% przy zaledwie 90 ms wzroście medianowego czasu nawigacji. Na urządzeniach z 2 GB RAM to była różnica między działającym prerender-em a jego automatycznym odrzuceniem.

Implementacja: list rules kontra document rules

Speculation Rules API oferuje dwa typy reguł, które warto łączyć. Reguły list to statyczny zestaw URL-i, świetne dla wygenerowanego serverside listingu, gdy dokładnie wiesz, jakie linki są na stronie. Reguły document (Chrome 121+) używają selektorów CSS i mikroformatów URL Pattern, dzięki czemu obejmują wszystkie linki pasujące do wzorca. Nawet te dodane dynamicznie przez SPA.

List rules: konkretne URL-e

<script type="speculationrules">
{
  "prefetch": [{
    "source": "list",
    "urls": [
      "/blog/inp-2026",
      "/blog/lcp-tuning",
      "/blog/bfcache"
    ],
    "eagerness": "moderate"
  }]
}
</script>

Document rules: filtrowanie po selektorze

<script type="speculationrules">
{
  "prerender": [{
    "source": "document",
    "where": {
      "and": [
        { "href_matches": "/product/*" },
        { "not": { "selector_matches": ".no-prerender" } },
        { "not": { "href_matches": "/product/*/reviews" } }
      ]
    },
    "eagerness": "moderate"
  }]
}
</script>

Zwróć uwagę na klauzule not. Używam ich, żeby wyłączyć strony z efektami ubocznymi (formularze, „mark as read"), które nie tolerują wywołania w tle. Reguła document jest reaktywna: Chrome obserwuje mutacje DOM i aktualizuje kandydatów, gdy Twój framework wstrzyknie nowe linki (Next.js router, HTMX swap).

Klasyczny problem: prefetchujesz /products?category=shoes, a użytkownik kliknie link z ?category=shoes&utm_source=email. Cache nie trafia, prerender idzie do kosza. Rozwiązanie to nagłówek HTTP No-Vary-Search (Chrome 122+), który mówi przeglądarce: „ten dokument nie zmienia się w zależności od tych parametrów query, potraktuj je jako identyczne".

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
No-Vary-Search: params=("utm_source" "utm_medium" "utm_campaign" "fbclid" "gclid")
Cache-Control: private, max-age=0

W konfiguracji Speculation Rules możesz wskazać oczekiwaną politykę expects_no_vary_search, żeby Chrome nie unieważniał prerender-a nawet zanim serwer zwróci nagłówek:

<script type="speculationrules">
{
  "prerender": [{
    "source": "list",
    "urls": ["/products?category=shoes"],
    "expects_no_vary_search": "params=(\"utm_source\" \"utm_medium\")",
    "eagerness": "eager"
  }]
}
</script>

Alternatywnie możesz zignorować wszystkie parametry poza whitelisted: params, except=("page" "sort"). Na jednym dużym marketplace obsługa No-Vary-Search podniosła nam współczynnik trafień prerender-a z 41% do 89%, bo ~55% ruchu przychodzi z linków z tagami analitycznymi.

Więcej szczegółów o współpracy tych nagłówków z warstwą CDN opisałem w artykule o strategiach cachowania w nowoczesnym webie. Pokazuję tam konfiguracje Fastly i Cloudflare Workers.

Jak debugować Speculation Rules w Chrome DevTools?

Bez narzędzi debugowanie prerender-ów to zabawa w ślepego kucharza. Na szczęście od Chrome 121 panel Application → Preloading pokazuje pełną historię reguł, statusy prerender-ów i konkretne powody porażek. W 2026 katalog obejmuje 40+ przyczyn (od MemoryLimitExceeded po MojoBinderPolicy).

Krok po kroku

  1. Otwórz stronę z tagiem <script type="speculationrules">.
  2. DevTools → Application → Preloading → Speculation Rules.
  3. Wybierz „Preloads", żeby zobaczyć status każdego URL: Ready, Running, Failure.
  4. Dla porażki kliknij URL. DevTools pokaże enum, np. NavigationBadHttpStatus lub PrerenderCancelledMemoryLimitExceeded.
  5. Zakładka „Speculations" listuje wszystkie zarejestrowane reguły z linkiem do skryptu źródłowego.

Programowo status sprawdzisz przez PerformanceObserver nasłuchujący navigation entries. Po aktywacji prerender-a pole activationStart jest dodatnie, a navigationType to prerender:

new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.activationStart > 0) {
      console.log("Prerender aktywowany po:", entry.activationStart, "ms");
      console.log("Typ nawigacji:", entry.type);
    }
  }
}).observe({ type: "navigation", buffered: true });

Wpływ prerender na analitykę i Core Web Vitals

Największa pułapka biznesowa: prerenderowana strona wykonuje JS, więc Twój tag Google Analytics odpali pageview zanim użytkownik cokolwiek zobaczy. W efekcie odsłony rosną nawet o 30–60%, a bounce rate spada sztucznie. Rozwiązanie to opóźnienie strzału do momentu aktywacji.

function sendPageview() {
  gtag("event", "page_view");
}

if (document.prerendering) {
  document.addEventListener("prerenderingchange", sendPageview, { once: true });
} else {
  sendPageview();
}

Ten wzorzec działa uniwersalnie. document.prerendering jest true tylko w kontekście spekulacyjnym, a prerenderingchange odpala się w momencie aktywacji (swap). Analogicznie obsługujesz wszystkie side-effecty: A/B testing, pixele reklamowe, video autoplay, WebSocket connection.

Web Vitals i activationStart

Biblioteka web-vitals w wersji 4+ automatycznie odejmuje activationStart od LCP i FCP. Jeśli używasz własnej instrumentacji, musisz to zrobić ręcznie, inaczej Twoje raporty pokażą LCP = 0 ms, co jest prawdą dla użytkownika, ale zaburza porównanie z ruchem bez prerender-a.

new PerformanceObserver((list) => {
  const nav = performance.getEntriesByType("navigation")[0];
  const activation = nav?.activationStart ?? 0;
  for (const entry of list.getEntriesByName("largest-contentful-paint")) {
    const lcp = Math.max(0, entry.startTime - activation);
    reportLcp(lcp, { prerendered: activation > 0 });
  }
}).observe({ type: "largest-contentful-paint", buffered: true });

Ta korekta pozwala Ci raportować dwa segmenty (LCP „real-user" z prerender-em i LCP „render" bez prerender-a), co jest istotne, bo Chrome UX Report bierze pod uwagę faktyczne doświadczenie, ale w RUM warto widzieć obie liczby. Więcej o granularnej analizie LCP piszę w tekście o optymalizacji LCP z Element Timing i atrybucją z pola.

Ograniczenia: Firefox, Safari, koszty pamięci i best practices

W sierpniu 2026 Speculation Rules API implementuje wyłącznie silnik Blink (Chrome, Edge, Opera, Samsung Internet, Brave). WebKit (Safari) i Gecko (Firefox) nie mają w roadmapach otwartego zobowiązania do implementacji, chociaż intent-to-implement dla prefetch w Firefox 130 był dyskutowany. To API traktuj jak progresywne wzmocnienie: nie łam funkcjonalności, jeśli reguła zostanie zignorowana.

Co jest zablokowane w kontekście prerender

  • Cross-origin popupy: window.open() jest odroczone do aktywacji.
  • Permissions API: żądania geolokalizacji, kamery, notyfikacji rzucają wyjątek.
  • Modyfikacja storage: zapisy do localStorage, IndexedDB, cookies są odroczone (Chrome buforuje i puszcza przy aktywacji).
  • Multimedia: <video autoplay> nie startuje w tle, użyj prerenderingchange.
  • Cross-site prerender: dostępny tylko dla origin === document.origin (chyba że włączysz eksperymentalny Speculation-Rules header).

Checklist wdrożeniowy

  1. Zacznij od prefetch z eagerness: moderate na najgorętszych ścieżkach, mierz zysk LCP przez tydzień.
  2. Dodaj No-Vary-Search dla parametrów marketingowych, patrz sekcja wyżej.
  3. Wprowadź prerender wyłącznie dla ścieżek z konwersją > 50% (checkout, „next article").
  4. Instrumentuj analitykę pod prerenderingchange, zanim wdrożysz na produkcji.
  5. Włącz alerty w RUM na prerender-status: failure. Nagły wzrost oznacza regresję pamięci albo błąd 4xx/5xx.
  6. Testuj kombinację z bfcache dla natychmiastowej nawigacji, obie warstwy razem dają najkrótsze możliwe TTFB przy powrotach i naprzód.

Warto też zajrzeć do oficjalnych zasobów: Chrome for Developers: Prerender pages zawiera aktualną tabelę powodów porażek, a MDN Speculation Rules API dokumentuje pełną gramatykę JSON.

Najczęściej zadawane pytania

Czy Speculation Rules API działa w Firefox i Safari?

Nie. W sierpniu 2026 API implementuje wyłącznie silnik Blink (Chrome 122+, Edge, Opera, Brave, Samsung Internet). Firefox i Safari po prostu ignorują tag <script type="speculationrules">, więc możesz go dodać bez ryzyka regresji. Dla użytkowników tych przeglądarek fall-back to tradycyjny rel="prefetch" lub Service Worker precache.

Jaka jest różnica między prerender a bfcache?

Bfcache (Back/Forward Cache) przywraca pełny stan strony po naciśnięciu wstecz/naprzód i działa po wizycie użytkownika. Prerender buduje stronę przed pierwszą wizytą, opierając się na przewidywaniu kolejnej nawigacji. Obie technologie uzupełniają się: bfcache dla nawigacji historycznej, Speculation Rules dla przewidywalnych „następnych kroków".

Ile stron mogę prerenderować jednocześnie?

Chrome w 2026 przyznaje twardy limit 2 aktywnych prerender-ów dla eagerness: immediate i eager oraz do 10 dla moderate i conservative. Nadmiarowe reguły trafiają do kolejki i uruchamiają się dopiero po zwolnieniu slotu. Przeglądarka może dodatkowo skrócić te limity na urządzeniach z Data Saver, Battery Saver lub pamięcią RAM poniżej 2 GB.

Czy prerender wywołuje zdarzenia analityczne przed aktywacją?

Tak. JavaScript wykonuje się w pełni podczas prerender-a, więc bez modyfikacji strzeliłbyś page_view zanim użytkownik cokolwiek zobaczy. Rozwiązanie to sprawdzenie document.prerendering i opóźnienie strzału do zdarzenia prerenderingchange. Biblioteka web-vitals v4+ oraz Google tag (gtag.js) v2026-05 obsługują to automatycznie.

Czy Speculation Rules zwiększają zużycie transferu na 4G?

Zależy od eagerness. immediate i eager generują transfer natychmiast, moderate dopiero przy hover / pointerdown, a conservative na pointerdown. Chrome dodatkowo automatycznie porzuca reguły w trybie Data Saver i Save-Data. W moich pomiarach reguła document z eagerness: moderate generuje dodatkowo ~4–8% transferu strony na sesję.

Alex Petrov
O Autorze Alex Petrov

Web performance engineer who treats every millisecond as a personal challenge. Has profiled more sites than he can count.