Navigation preload: hiding service worker boot time from navigations
In one line: Navigation preload lets the browser start fetching a navigation request in parallel with service worker boot-up, so a worker that has to cold-boot is no worse off than one that is already running.
The problem it solves
Section titled “The problem it solves”When a user navigates to a page controlled by a service worker, the browser must boot the service worker (if it is not already running), send it a fetch event, and wait for the result. A service worker cannot process events until it has finished booting, and when a worker does need to boot from scratch, that startup delay can slow down the response to a navigation request — even though the eventual response might come from a cache, which is normally very fast.
Enabling it
Section titled “Enabling it”Navigation preload is enabled with NavigationPreloadManager.enable(), typically in the service worker’s activate event handler, with feature detection via ServiceWorkerRegistration.navigationPreload:
addEventListener("activate", (event) => { event.waitUntil( (async () => { if (self.registration.navigationPreload) { await self.registration.navigationPreload.enable(); } })(), );});self.registration.navigationPreload returns the NavigationPreloadManager object if supported, or undefined otherwise.
Instance methods
Section titled “Instance methods”| Method | Purpose |
|---|---|
enable() |
Enables navigation preloading; returns a Promise resolving to undefined. |
disable() |
Disables navigation preloading; returns a Promise resolving to undefined. |
setHeaderValue() |
Sets the value of the Service-Worker-Navigation-Preload HTTP header sent with preload requests; returns an empty Promise. |
getState() |
Returns a Promise resolving to an object indicating whether preloading is enabled and what header value is used. |
Using the preloaded response
Section titled “Using the preloaded response”Navigation preload must be used together with a fetch event handler: the preloaded result is read from FetchEvent.preloadResponse, a Promise that resolves to a Response (or undefined if preload did not run for that request):
addEventListener("fetch", (event) => { event.respondWith( (async () => { const cachedResponse = await caches.match(event.request); if (cachedResponse) return cachedResponse;
const response = await event.preloadResponse; if (response) return response;
return fetch(event.request); })(), );});preloadResponse resolves to a Response only when navigation preload is enabled for the service worker, the request is a GET request, and the request is a navigation request; otherwise it resolves to undefined. It is only available inside service workers.
The Service-Worker-Navigation-Preload header
Section titled “The Service-Worker-Navigation-Preload header”The browser automatically sends the Service-Worker-Navigation-Preload header (default value true) with preload requests, letting a server distinguish preload requests from normal ones. setHeaderValue() can change this value, for example to a cache-version ID:
navigator.serviceWorker.ready .then((registration) => registration.navigationPreload.setHeaderValue(newValue), ) .then(() => { console.log("Done!"); });If the normal and preload responses can differ, the server must set Vary: Service-Worker-Navigation-Preload so caching behaves correctly.
Browser support
Section titled “Browser support”NavigationPreloadManager and FetchEvent.preloadResponse require a secure context (HTTPS) and are Baseline widely available — well established and supported across many devices and browsers since April 2022.
Practical checklist
Section titled “Practical checklist”- Enable navigation preload in the
activatehandler, guarded by feature detection. - Await
event.preloadResponseinside thefetchhandler; do not use the response elsewhere. - Keep a preload request alive with
event.waitUntil()if you end up not using its response (for example, after a cache hit). - Set
Vary: Service-Worker-Navigation-Preloadon the server if preload and normal responses can differ.
Cross-references
Section titled “Cross-references”- Service worker lifecycle — the
activateevent where navigation preload is enabled - Fetch event handling — the
fetchevent wherepreloadResponseis read