Service Worker Static Routing API Origin Trial

Brendan Kenny
Brendan Kenny

Service workers are a powerful tool for allowing websites to work offline and create specialized caching rules for themselves. A service worker fetch handler sees every request from a page it controls, and can decide if it wants to serve a response to it from the service worker cache, or even rewrite the URL to fetch a different response entirely—for instance, based on local user preferences.

However, there can be a performance cost to service workers when a page is loaded for the first time in a while and the controlling service worker isn't currently running. Since all fetches need to happen through the service worker, the browser has to wait for the service worker to start up and run to know what content to load. This startup cost can be small, but significant, for developers using service workers to improve performance through caching strategies.

Navigation preload is one approach to solving the problem—allowing navigation requests to be made over the network in parallel to service worker startup—but it's limited to initial navigation requests and still includes the service worker in the critical path. Since navigation preload launched, there have been multiple efforts to develop a more general solution to the problem space, including ways for some requests to not be blocked on service worker startup at all.

The Service Worker Static Routing API

Starting in Chrome 116, the experimental Service Worker Static Routing API is available for testing the first step to such a solution. When a service worker is installed, it can use the Service Worker Static Routing API to declaratively state how certain resource paths should be fetched.

In the initial version of the API, paths can be declared to always be served from the network, not the service worker. When a controlled URL is later loaded, the browser can start fetching resources from those paths before the service worker has finished starting. This removes the service worker from the paths that you know don't need a service worker.

To use the API, the service worker calls event.registerRouter on the install event with a set of rules:

self.addEventListener('install', event => {
  if (event.registerRouter) {
    // Go straight to the network and bypass invoking "fetch" handlers for all
    // same-origin URLs that start with '/form/'.
    event.registerRouter([{
      condition: {
        urlPattern: {pathname: '/form/*'},
      },
      source: 'network',
    }]);
  }
});

Each rule generally has two properties:

  • condition: specifies when the rule applies using the URL Pattern API to match resource paths. The property can take a URLPattern instance, or the equivalent plain object that is compatible with being passed into the URLPattern constructor (for instance, either new URLPattern({pathname: '*.jpg'}) or just {pathname: '*.jpg'}).

    The flexibility of URL Patterns means that the rule can match something as simple as any resource under a path, to very specific and detailed conditions. The patterns should generally be familiar to users of popular routing libraries.

  • source: specifies how the resources matching condition will be loaded. Today, only the 'network' value is supported (bypassing the service worker to load the resource over the network directly), but the plan is to expand this to other values in the future.

Use cases

As explained, the initial version of the API is essentially an escape hatch from service worker control for some paths. Where this will make sense to use is going to be dependent on how you use your service worker and how users traverse your site.

One example might be if your site uses a cache-first strategy (falling back to network), but there's some content that's so rarely visited that there's little to no value in it ever hitting the cache (like archived content or RSS feeds). Restricting these paths to be fetched from the network only can be set up in the service worker, but the service worker still has to start up and run to decide how to handle those requests.

The static routing API, in contrast, bypasses the service worker completely with a few declarative lines:

self.addEventListener('install', event => {
  if (event.registerRouter) {
    event.registerRouter([{
      condition: {
        urlPattern: {pathname: '/feeds/*.xml'},
      },
      source: 'network',
    }, {
      condition: {
        urlPattern: {pathname: '/archives/*'},
      },
      source: 'network',
    }]);
  }
});

As the Service Worker Static Routing API evolves, the plan is for this configuration to get more flexible and support more scenarios, like declaratively racing a network fetch and service worker startup. See the spec explainer's exploration of a potential "final form" of the API for more details.

Trying out the Service Worker Static Routing API

The Service Worker Static Routing API is available in Chrome starting in version 116 behind an origin trial, which allows developers to test out the API on their site with real users to measure the effect. See "Get started with origin trials" for background information on origin trials.

For local testing, the Service Worker Static Routing API can be enabled with a flag at chrome://flags/#service-worker-static-router, or by running Chrome from the command like with --enable-features=ServiceWorkerStaticRouter.

Feedback and future directions

The Service Worker Static Routing API is actively being developed and still being shaped. If it seems like it could be useful for you, please try it out via the origin trial and provide feedback on the API, implementation, and available functionality.