Skip to main content

Respond to browsing requests without waiting on the web using a service worker.

It appears in:
Network reliability

Navigation requests are requests for HTML documents made by your browser each time you enter a new URL in the navigation bar or follow a link on a page that takes you to a new URL. This is where service workers have their greatest impact on performance: By using a service worker to respond to browsing requests without waiting for the web, you can ensure that navigations are fast and reliable, as well as being resilient when the network is not available. This is the largest performance gain that comes from a service worker, compared to what is possible with HTTP caching.

As detailed in the Identify Resources Loaded from the Network guide, a browse request is the first of many requests potentially made on the
of network traffic. The HTML that you load through a navigation request initiates the flow of all other sub-resource requests such as images, scripts, and styles.

Inside a service worker fetch event handler, you can determine if a request is a navigation by checking the request.mode property in the FetchEvent. If it is configured for 'navigate', then it is a navigation request.

As a general rule, do not use long-lasting Cache-Control headers to cache the HTML response for a browse request. They should normally be satisfied over the network, with
Cache-Control: no-cache, to ensure that the HTML, along with the chain of subsequent network requests, is (reasonably) up to date. Go against the net every time the user navigates to a new page, unfortunately it means that every navigation might Be slow At the very least, it means it won't be surely Quick.

Cache-Control: no-cache means that the browser must verify (or "revalidate") with the server before using a previously cached resource. This requires a round-trip network communication to complete before the resource can be used.

Different approaches to architectures

To find out as Responding to browsing requests by avoiding the web can be difficult. The correct approach largely depends on the architecture of your website and the number of unique URLs that users can access.

While there is no one-size-fits-all solution, the following general guidelines should help you decide which approach is the most viable.

Small static sites

If your web application consists of a relatively small number (think: a couple dozen) of unique URLs, and each of those URLs corresponds to a different static HTML file, then a viable approach is to cache all of those HTML files and respond to navigation requests with the appropriate cached HTML.

With caching, you can cache the HTML in advance, as soon as the service worker is installed, and update the cached HTML each time you rebuild your site and redeploy your service worker.

Alternatively, if you prefer to avoid caching all of your HTML, perhaps because users tend to navigate only to a subset of URLs on your site, you can use a stale runtime caching strategy while revalidating. However, be careful with this approach, as each individual HTML document is cached and updated separately. Using runtime caching for HTML is more appropriate if you have a small number of URLs that get checked frequently by the same set of users, and if you are comfortable with revalidating those URLs independently of each other.

Single page apps

Modern web applications often use a single page architecture. In it, client-side JavaScript modifies the HTML in response to user actions. This model uses the History API to modify the current URL as the user interacts with the web application, leading to what is effectively 'mock' browsing. While subsequent browsing may be "fake", the initial browsing is real and it is still important to ensure that it is not blocked on the web.

Fortunately, if you are using the single page architecture, there is a simple pattern to follow to serve the initial navigation from the cache: the application shell. In this model, your service worker responds to navigation requests by returning the same unique HTML file that has already been cached, regardless of the requested URL. This HTML should be basic and consist, perhaps, of a generic loading indicator or skeleton content. Once the browser has loaded this HTML from cache, your existing client-side JavaScript takes over and displays the correct HTML content for the URL of the original browse request.

Workbox provides the tools you need to implement this approach; the navigateFallback

allows you to specify which HTML document to use as your application's shell, along with an optional allow and deny list to limit this behavior to a subset of your URLs.

Multi-page apps

If your web server generates the HTML for your site dynamically, or you have more than a few dozen unique pages, then it is much more difficult to bypass the net when handling browsing requests. The advice in Everything Else likely applies to you.

But for a certain subset of multi-page applications, you might be able to implement a service worker that fully replicates the logic used on your web server to generate HTML. This works best if you can share routing information and templates between the server and service worker environments, and in particular if your web server uses JavaScript (without relying on
Node.js-specific features, such as file system access).

If your web server falls into that category and you would like to explore an approach to move HTML generation off the network to your service worker, the guide at Beyond SPAs: Alternative Architectures for Your PWA can help you get started.

Everything else

If you are unable to respond to cached HTML navigation requests, you should take steps to ensure that adding a service worker to your site (to handle other non-HTML requests) does not slow down your navigations. Starting the service worker without using it to respond to a browse request will introduce a small amount of latency (as explained in Building faster, more resilient applications with Service Worker). You can mitigate this overhead by enabling a function called navigation preload, and then using network response
that has been preloaded within your fetch event handler.

Workbox provides a helper library that function detects whether navigation preload is supported and, if so, simplifies the process of telling your service worker to use network response.

Photo by Aaron Burden in Unsplash,