A Service Worker sits between your app and the network as a programmable proxy — the same mechanism that enables offline support also enables you to shoot yourself in the foot by caching something you needed fresh, which is why cache strategy deserves as much attention as the offline feature itself.
A Service Worker is a script that runs separately from your web page, intercepting network requests and enabling offline functionality, background sync, and push notifications. Combined with a web app manifest, it's what turns a website into an installable Progressive Web App (PWA) — one that can work offline and be added to a device's home screen like a native app.
Why Service Workers & PWA Matter (and When to Skip Them)
For applications where offline access or installability genuinely matters to users — content that should be available without connectivity, or an app-like experience users want on their home screen — Service Workers provide capabilities a regular web page can't. They're also the mechanism behind background sync and push notifications on the web.
Skip Service Workers if your application has no meaningful offline use case and doesn't need installability — the added complexity (cache invalidation, versioning, debugging a script running outside the normal page lifecycle) isn't worth it without a concrete need driving it.
Getting Started with Service Workers & PWA
Registering a service worker:
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}
A basic cache-first strategy in the service worker:
// sw.js
const CACHE_NAME = "app-cache-v1";
const urlsToCache = ["/", "/styles.css", "/app.js"];
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(urlsToCache))
);
});
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request).then((cached) => cached || fetch(event.request))
);
});
A web app manifest for installability:
{
"name": "Your App",
"short_name": "App",
"start_url": "/",
"display": "standalone",
"icons": [{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }]
}
Core Service Workers & PWA Concepts Every Developer Should Know
Different caching strategies suit different content types. Cache-first works for static assets that rarely change; network-first (falling back to cache) suits content that should be fresh when possible but usable offline as a fallback; stale-while-revalidate serves cached content immediately while updating the cache in the background for next time.
Service worker versioning requires deliberate cache invalidation. Simply updating your service worker script doesn't automatically clear old caches — you need to explicitly delete outdated cache versions on activation, or users can end up stuck with stale cached content indefinitely.
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
)
);
});
Service workers have their own lifecycle independent of the page, including an update process that can leave an old version active until all tabs using it are closed — worth understanding when debugging "why isn't my update showing up" issues, a very common point of confusion.
Installability requires specific manifest and service worker criteria to be met (HTTPS, a valid manifest, a registered service worker with a fetch handler) — browsers won't offer the install prompt without these baseline requirements satisfied.
Common Service Worker Mistakes and How to Fix Them
Mistake 1: caching everything indiscriminately, including content that needs to always be fresh (like authenticated API responses). This can serve stale or incorrect data. Fix: apply caching strategies deliberately per content type, not universally.
Mistake 2: not handling cache versioning, leaving users stuck on stale cached content after deploys. Fix: version your cache name and explicitly clean up old versions on the activate event.
Mistake 3: registering a service worker without understanding its update lifecycle, causing confusion when changes don't appear to take effect immediately. Fix: understand the install/waiting/activate lifecycle, and consider skipWaiting() and clients.claim() for cases where immediate updates are more important than avoiding disruption to open tabs.
When Should You Use a Service Worker Instead of Just Relying on HTTP Caching?
Use a Service Worker when you need offline functionality, fine-grained programmatic control over caching behavior, or PWA installability — capabilities HTTP caching alone can't provide. Rely on standard HTTP caching (Cache-Control headers) for the common case of simply making repeat requests faster, which is simpler and doesn't carry service worker's added lifecycle complexity.
Service Workers & PWA in Production
Test the update flow explicitly — deploy a change and verify users actually receive it in a reasonable time, since a misconfigured cache strategy can leave users on stale versions of your app indefinitely without any visible error. Also monitor for service worker registration failures, since a broken service worker can silently degrade the experience for affected users without an obvious error surfaced anywhere else.
Before shipping a service worker to production, verify your cache invalidation strategy actually works across a real deploy cycle — that's the part most likely to cause a confusing, hard-to-diagnose issue if untested.