Skip to content

API in Service Worker

Service Worker Static Routing API

Published Updated

The Service Worker Static Routing API lets a service worker declare, at install time, which requests the browser should serve from the network or from a cache without running the worker to fetch them. The declaration is a list of rules passed to event.addRoutes() in the install handler, and each rule pairs a condition on the request with the source the response should come from.

addRoutes(routerRules)

routerRules is a single rule object or an array of them, and the method returns a promise that fulfills with undefined. The method is on InstallEvent, so it is reachable from the install handler, and a rule set therefore belongs to the worker version that installed it. The specification notes that event.addRoutes(rules) extends the lifetime of the event by default, as if event.waitUntil(promise) had been called.

BCD api.InstallEvent.addRoutes records support from Chrome 123 and Safari 27, mirrored into the Chromium-based browsers and the Safari derivatives it lists, and no support in Firefox, where it points at Mozilla bug 1855580 for the implementation status. Chrome’s launch post describes the API as available from Chrome 123 after an origin trial that began in Chrome 116 under the name registerRouter(), a method the post marks deprecated with removal planned for Chrome 125.

A rule is { condition, source }. The condition keys below are the ones MDN’s addRoutes() reference documents; when several are present in one condition, a resource must meet all of them to match the rule.

Condition key Value Matches
urlPattern a URLPattern instance, or a constructor input pattern the URLs that match the rule. Regular expression capturing groups are not allowed, so URLPattern.hasRegExpGroups has to be false.
requestMethod string, such as "get", "put" or "head" the HTTP method the request is sent by.
requestMode string, such as "same-origin", "no-cors" or "cors" the mode the request has.
requestDestination string, such as "audio", "document", "script" or "worker" the destination of the request, that is, the content type requested.
runningStatus "running" or "not-running" the required running status of the service worker.
or an array of condition objects one set of the nested conditions. Conditions inside or are mutually exclusive with other conditions.
not a condition object conditions that must explicitly not be met. Conditions inside not are mutually exclusive with other conditions.

source is an enumerated value or an object. MDN’s enumerated values are "cache", "network", "fetch-event" and "race-network-and-fetch-handler", the last of which attempts the network and the fetch event handler simultaneously and uses whichever completes first. MDN pairs "fetch-event" with the runningStatus condition to load a resource through the worker while it is running and fall back to a static route on the network while it is not. The object form carries a single cacheName property naming a cache; MDN notes that if that cache does not exist the browser defaults to the network so the requested resources can still be obtained when the network is available.

MDN lists these causes for a TypeError: a rule object inside routerRules that is invalid, a source of "fetch-event" on a worker with no fetch event handler, and or combined with another condition type.

  • An invalid rule rejects the call that carried it, so validate a generated rule list before shipping it.
  • "fetch-event" and "race-network-and-fetch-handler" depend on a registered fetch handler. The addRoutes() algorithm returns a promise rejected with a TypeError when source is either "fetch-event" or "race-network-and-fetch-handler" and the service worker’s set of event types to handle does not contain fetch. Rules whose source is "network", "cache" or the cacheName object are not named by that step.
  • A condition that carries or next to a sibling key has to be rewritten as two rules, one per branch, each repeating the sibling. MDN’s own example of that rewrite pairs urlPattern with requestMethod twice.
  • The specification also rejects the promise with a TypeError when the Check Router Registration Limit algorithm, run over a copy of the worker’s existing list of router rules with the new rules appended, returns false.

An empty condition object is a rejection rather than a rule matching everything, which is the first string above.

The examples below are the shapes most apps need: bypass the worker on a path, declare routes where the method exists, and serve image extensions from a named cache.

Sending a path to the network when the worker is cold

Section titled “Sending a path to the network when the worker is cold”

The condition combines a URL pattern with runningStatus, so the rule applies while the service worker is not running, which is the case that costs a start-up.

addEventListener('install', (event) => {
event.addRoutes({
condition: {
urlPattern: '/articles/*',
runningStatus: 'not-running',
},
source: 'network',
});
});

While the worker is running this rule does not match, and the request reaches the fetch handler, so a cache-first strategy for the same path still applies on warm navigations. Chrome’s launch post shows the mirror-image rule, runningStatus: "running" with source: "fetch-event".

Declaring routes where the method exists, with a fallback

Section titled “Declaring routes where the method exists, with a fallback”

addRoutes() is a method on the event object, so it can be tested before use. The absent branch is not a degraded mode: it is the behaviour a service worker had before static routes, where the fetch handler decides everything.

addEventListener('install', (event) => {
if (!('addRoutes' in event)) {
// No addRoutes() on this event: leave the routing to the fetch handler.
return;
}
event
.addRoutes([
{ condition: { urlPattern: '/form/*', requestMethod: 'post' }, source: 'network' },
{ condition: { requestDestination: 'font' }, source: { cacheName: 'fonts' } },
])
.catch((error) => {
console.error('static routes rejected, using the fetch handler instead', error);
});
});

Keep the catch. The method reports its failures by rejecting rather than throwing synchronously, so a rejection with no handler leaves the app running on the fetch handler alone and surfaces as an unhandled promise rejection.

Serving two image extensions from one named cache

Section titled “Serving two image extensions from one named cache”

or takes a list of conditions and is mutually exclusive with other conditions, so the extensions go inside the array and the shared source stays outside it.

addEventListener('install', (event) => {
event.addRoutes({
condition: {
or: [{ urlPattern: '*.png' }, { urlPattern: '*.jpg' }],
},
source: { cacheName: 'pictures' },
});
});

To narrow the same two patterns to GET requests, write two rules, each with its own urlPattern and requestMethod, which is the rewrite MDN gives for this case.

Chrome DevTools shows what the browser registered: Chrome’s launch post states that registered router rules are displayed in the Service Worker tab of the Application panel, and that in the Network panel a request matching a registered rule is indicated in the size column, with the registered router ID shown on pointer hover.

Specifications

SpecificationStatus
Service Workers: the addRoutes() methodW3C draft