# Navigation preload: hiding service worker boot time from navigations

> How the NavigationPreloadManager lets the browser start fetching a navigation request in parallel with service worker boot-up, how to enable it and read FetchEvent.preloadResponse, the Service-Worker-Navigation-Preload header, and its Baseline widely-available status since April 2022.

**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

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

Navigation preload is enabled with `NavigationPreloadManager.enable()`, typically in the service worker's `activate` event handler, with feature detection via `ServiceWorkerRegistration.navigationPreload`:

```js
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

| 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

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):

```js
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

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:

```js
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

`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

- [ ] Enable navigation preload in the `activate` handler, guarded by feature detection.
- [ ] Await `event.preloadResponse` inside the `fetch` handler; 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-Preload` on the server if preload and normal responses can differ.

## Cross-references

- [Service worker lifecycle](/reference/service-worker/lifecycle/) — the `activate` event where navigation preload is enabled
- [Fetch event handling](/reference/service-worker/fetch-event/) — the `fetch` event where `preloadResponse` is read