Service worker is a JavaScript worker that sits between a web page and the network, so it can intercept requests, serve cached responses and keep a site usable offline. It is defined by the W3C Service Workers specification and is the core building block of a PWA. Service workers only run in secure contexts: HTTPS, or localhost for local development.
A page calls navigator.serviceWorker.register() with the URL of a script. The browser downloads it, runs it in a separate worker thread with no access to the DOM, and moves it through a fixed set of states: parsed, installing, installed, activating, activated and redundant. Three events matter most:
event.waitUntil() to pre-cache files; the worker is not treated as installed until the promises resolve successfully.skipWaiting().event.respondWith() to answer from a cache, the network, or a mix of both.A new worker does not control the page that registered it. The page needs a reload, or the worker must call clients.claim().
Scope limits which pages a worker controls. By default the maximum scope is the folder that holds the script, so /js/sw.js can only control /js/ and below. A server can widen this by sending a Service-Worker-Allowed response header on the script.
Updates are byte-based. When the browser checks for an update, it fetches the script again and compares it byte for byte with the installed copy. With the default updateViaCache: 'imports', that script fetch always skips the HTTP cache. A registration is stale once more than 86,400 seconds (24 hours) have passed since its last update check, and from then on imported scripts skip the HTTP cache too.
// sw.js: cache two files on install, answer from cache first
self.addEventListener('install', (event) => {
event.waitUntil(caches.open('v1').then((c) => c.addAll(['/', '/hello.txt'])));
});
self.addEventListener('fetch', (event) => {
event.respondWith(caches.match(event.request).then((hit) => hit || fetch(event.request)));
});
Run in headless Chromium against a local server, then reloaded with the server shut down, the page still got a response:
register() -> scope http://localhost:8765/, isSecureContext true
after reload -> navigator.serviceWorker.controller is set
fetch /hello.txt (server stopped) -> 200 hello from cache
register('/js/sw.js', {scope: '/'}) -> SecurityError: ... The path of the provided scope ('/') is not under the max scope allowed ('/js/').
Yes. Service workers are available only in secure contexts, so the page must be served over HTTPS. Browsers treat localhost as secure, which is why local development works without a certificate. The restriction exists because a worker can rewrite every response for its scope.
SecurityError ... not under the max scope allowed. Serve the script from the site root or send Service-Worker-Allowed.skipWaiting() only when you know it is safe.caches.open('v1') entries live until you delete them. Name caches by version and delete old ones in the activate handler.updateViaCache: 'imports', files pulled in by importScripts() can come from the HTTP cache until the registration goes stale. Version their URLs.localStorage) cannot be used. Use the Cache API, and postMessage to talk to pages.