Glossary

Definition

Service worker update cycle

Short definition

Browsers check a registered service worker's script for updates on every navigation to a page in scope, and at least every 24 hours in the background, by re-fetching the script URL and comparing it byte-for-byte against the currently installed version. Any difference — even a single changed character or comment — triggers a fresh install/waiting/activate cycle for a new worker instance.

Reviewed July 29, 2026

Because the comparison is byte-level, cache-busting the service worker script itself with a hashed filename is counterproductive: the browser needs a stable URL to compare against over time, so sw.js should generally not be renamed on every deploy the way app.[hash].js commonly is.

Serving the service worker script with a long Cache-Control max-age can delay update detection for up to that duration, since the browser's update check itself is subject to HTTP caching; most guidance recommends Cache-Control: no-cache or a short max-age specifically for the worker script.

You can force an immediate check from the page with registration.update(), useful right after a user action like re-focusing the tab, without waiting for the browser's own periodic schedule.

The full lifecycle — installing, waiting, activating, activated, redundant — is documented on MDN: https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker/state.

Related terms
Service worker registration
Registration happens when a page calls navigator.serviceWorker.register("/sw.js", { scope: "/app/" }). The scope option (defaulting to the directory the script is served from) determines which pages the worker is allowed to control — a worker at /sw.js served with no scope option can normally only control /*, but serving it with the Service-Worker-Allowed HTTP header lets it claim a scope above its own script location.
waiting state (service worker)
A service worker enters the waiting state after install succeeds but before it can activate, because the browser only replaces an active worker once every page it currently controls has been closed or navigated away from. It stays here indefinitely until those pages close or the code forces the transition with skipWaiting().
skipWaiting()
self.skipWaiting() is called inside a service worker's install (or activate) handler to skip the normal waiting state and activate immediately, even while older pages controlled by a previous worker are still open. It is usually paired with clients.claim() so the new worker also starts controlling those pages right away.
Cache Storage API
The Cache Storage API, exposed as the global caches object, lets a service worker (or page) store Request/Response pairs in named caches, e.g. caches.open("shell-v1").then((cache) => cache.put("/logo.png", response)). Unlike the HTTP cache, it is fully scriptable, has no automatic eviction schedule tied to headers, and persists until explicitly deleted or the storage quota is exceeded.
Cache-first strategy
Cache-first is a fetch-handling strategy: event.respondWith(caches.match(event.request).then((cached) => cached ?? fetch(event.request))). It serves cached content instantly and skips the network entirely when a match exists, making it ideal for versioned, immutable assets such as hashed JS/CSS bundles or app-shell icons that never change once deployed.
Network-first strategy
Network-first is a fetch-handling strategy: event.respondWith(fetch(event.request).then((res) => { cache.put(event.request, res.clone()); return res; }).catch(() => caches.match(event.request))). It prioritises up-to-date data and only serves a cached copy if the network request fails outright, making it the standard choice for API calls and frequently changing pages.