Service worker

On this page 7

A service worker is an event-driven script that the browser runs in the background, separate from any page, and that sits between a web application and the network as a programmable proxy. In browser JavaScript, it intercepts fetch requests from the pages it controls and decides whether to answer them from a cache, from the network or from generated content.

Service workers are the technical foundation of offline-capable web applications, background synchronization and push notifications. They require a secure context (HTTPS), except on localhost, and they have no access to the DOM, so they communicate with pages only through messages.

How a service worker intercepts the network

A page registers a service worker with navigator.serviceWorker.register('/sw.js'). The browser downloads the script, runs it in its own thread and associates it with a scope, by default the directory where the script lives. Every page whose URL falls inside that scope becomes a controlled client once the worker is active.

When a controlled page requests a resource, the browser dispatches a fetch event to the worker instead of going straight to the network. The handler can call event.respondWith() with any Promise that resolves to a Response. If it does not respond, the request continues to the network as usual, which makes the worker transparent by default.

The worker is not permanently running. The browser starts it when an event arrives and terminates it after a short idle period, typically tens of seconds, which means global variables do not persist between events. State must live in the Cache API or IndexedDB.

The service worker lifecycle

The lifecycle is designed so that only one version controls pages at a time. A new script never takes over pages that an older version is serving unless the developer asks for it explicitly, which prevents a page from loading half its assets with old logic and half with new.

PhaseTriggerTypical work
Registrationregister() call from a pageBrowser fetches and parses the script
InstallFirst successful download of a new versionPrecache the application shell
WaitingOlder version still controls open pagesNothing until those pages close
ActivateNo clients use the old versionDelete outdated caches, migrate storage
Idle and fetchRequests and messages from clientsServe responses, handle events
RedundantReplaced or install failedWorker is discarded
Lifecycle phases and what happens in each

The browser checks for updates on navigation and at least every 24 hours, comparing the script byte by byte. Calling self.skipWaiting() during install and clients.claim() during activate shortens the waiting phase, but it can mix versions in already open tabs, so it should be used only when old and new caches are compatible.

Caching strategies a service worker implements

The service worker itself does not cache anything automatically. The developer chooses a caching strategy per request type, and the choice determines freshness, speed and offline behavior.

StrategyBehaviorBest suited for
Cache firstUse the cache, fall back to networkVersioned static assets and fonts
Network firstUse the network, fall back to cacheHTML pages and frequently changing data
Stale while revalidateServe cache now, update it in the backgroundAvatars, non-critical API data
Network onlyNever cachePayments, analytics, authenticated mutations
Cache onlyNever touch the networkPrecached application shell
Common caching strategies

Network first should include a timeout of a few seconds before falling back to the cache; otherwise a slow connection, often called lie-fi, leaves the user waiting longer than a true offline state would. Cache storage is subject to browser quotas that depend on available disk space, and the browser may evict it under storage pressure unless persistent storage is granted.

A service worker is a specialized kind of worker. It shares the off-main-thread execution model of a web worker but differs sharply in lifetime, scope and purpose. It also builds on the Fetch API and on Promise-based async/await code throughout.

TechnologyLifetimeMain purpose
Service workerEvent-driven, outlives pagesNetwork proxy, offline, push, sync
Web workerTied to the page that created itHeavy computation off the main thread
Shared workerLives while any connected page is openShared state between tabs of one origin
HTTP cacheManaged by the browserAutomatic reuse based on headers
Fetch APIPer requestMaking requests from any script
Service workers and neighboring concepts

The HTTP cache still works underneath a service worker. A request that the worker forwards with fetch() can be satisfied by the HTTP cache, so header policy and worker strategy must be designed together to avoid serving a resource that is stale in both layers.

Pitfalls and debugging

Most service worker incidents come from stale code that refuses to leave. Because the worker controls pages before they load, a buggy version can keep serving broken assets until it is replaced, which is why a kill switch strategy should exist before the first deployment.

  • Caching the worker script itself: serve sw.js with Cache-Control no-cache so updates are detected.
  • Unversioned cache names: activate should delete old caches by name, or storage grows indefinitely.
  • Too wide a scope: a worker at the site root controls every path, including unrelated sections.
  • Opaque responses: cross-origin responses without CORS count heavily against storage quota.
  • Forgetting `event.waitUntil()`: the browser may terminate the worker before asynchronous work finishes.

Browser developer tools expose an Application panel where developers can bypass the worker for the network, force updates, unregister workers and inspect cache contents, which turns most of these issues into quick diagnoses.

The short version

A service worker is like a personal assistant standing at the door of a website. Every time the page asks for a file, the assistant decides whether to fetch it from the internet or hand over a copy it saved earlier.

Because the assistant keeps those copies, the site can still open without a connection, and it often opens faster. The assistant also receives messages while the page is closed, which is how websites can show notifications.

Where it makes a difference

A field inspection team uses a web application on tablets in warehouses where mobile coverage drops out regularly. Before the change, a lost signal meant a blank error page and forms that lost their data.

The developers add a service worker that precaches the application shell of roughly 30 files during install, uses network first with a three-second timeout for inspection lists and stores unsent form submissions in IndexedDB. A background sync event replays those submissions when the connection returns.

Inspectors now open the application instantly even in dead zones, complete their checklists offline and see entries upload automatically later. The outcome is no lost inspections and noticeably faster repeat loads, because the shell is served from local cache instead of the network.