Back to Browser Internals
Browser Internals
easy
mid

What is a service worker and what role does it play in the browser?

A service worker is a script the browser runs in the background, separate from the page, that intercepts network requests and can serve from cache, the network, or both. Enables offline mode, custom caching strategies (cache-first, network-first, stale-while-revalidate), background sync, push notifications. Lifecycle: install → activate → fetch event. Constraints: HTTPS only, no DOM access, single-threaded JS, scope = path of registration. Powers PWAs.

8 min read·~5 min to think through

A service worker (SW) is a browser-managed JS worker that sits between your page and the network. It can intercept fetch requests and decide whether to serve from cache, the network, or generate a response.

What it enables

  • Offline mode — serve cached HTML/assets when the network is down.
  • Custom caching strategies beyond HTTP cache (e.g., stale-while-revalidate, cache-first for static, network-first for API).
  • Background sync — queue mutations made offline; replay when network returns.
  • Push notifications — receive server-sent pushes even when the tab is closed.
  • Periodic background sync (limited support) — wake up periodically to refresh.
  • Pre-cache the next likely navigation's assets.

Lifecycle

  1. Register (from the page):

``js navigator.serviceWorker.register('/sw.js'); ``

  1. Install event fires once per SW version. Use it to pre-cache static assets.
  2. Activate event fires once the new SW takes control. Use it to clean up old caches.
  3. Fetch events fire for every network request in scope.

A new SW deployment doesn't immediately replace the old one — by default the old SW keeps serving open tabs. self.skipWaiting() + clients.claim() force an immediate takeover (risky if old + new SW have different cache schemas).

Anatomy

js
// sw.js
const CACHE = 'v3-static';
const STATIC = ['/', '/styles.css', '/app.js', '/offline.html'];

self.addEventListener('install', e => {
  e.waitUntil(caches.open(CACHE).then(c => c.addAll(STATIC)));
});

self.addEventListener('activate', e => {
  e.waitUntil(
    caches.keys().then(keys =>
      Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k)))
    )
  );
});

self.addEventListener('fetch', e => {
  if (e.request.mode === 'navigate') {
    // Network-first for HTML
    e.respondWith(
      fetch(e.request).catch(() => caches.match('/offline.html'))
    );
    return;
  }
  // Cache-first for static assets
  e.respondWith(
    caches.match(e.request).then(hit => hit || fetch(e.request))
  );
});

Caching strategies

StrategyWhen to use
Cache-firstVersioned static assets (hashed JS/CSS, images).
Network-firstHTML, API responses where freshness matters.
Stale-while-revalidateResources where you want fast cache hit + background update.
Network-onlyAuth, mutations.
Cache-onlyPre-cached fallback (offline page).

Constraints

  • HTTPS required (except localhost).
  • No DOM access — workers run in a separate global; communicate with pages via postMessage.
  • Scope = the path of the SW file. /sw.js controls the whole origin; /app/sw.js only /app/*.
  • One SW per scope at a time.
  • Lifespan is browser-controlled — the SW gets killed when idle, woken on events.
  • Storage quotas — Cache API uses origin storage; can be evicted under pressure.

Common pitfalls

  • Cache invalidation: forgetting to bump the cache name on deploy → users stuck on old assets.
  • SW serving an old HTML shell that loads new chunked JS that no longer exists → app breaks.
  • Auth headers in cached requests — make sure SW caches don't leak across users.
  • CORS in SW fetch — opaque responses can be cached but JS can't inspect them.
  • Debuggingchrome://serviceworker-internals, DevTools → Application → Service Workers, Update on reload.

Libraries

Don't write SW from scratch. Use Workbox (Google) — battle-tested helpers for caching strategies, route matching, precache manifests, build-tool integrations. Next.js, Nuxt, Vite all have PWA plugins built on Workbox.

When you don't need a service worker

  • App is online-only and has no offline use case.
  • HTTP caching is sufficient (most sites).
  • The SW complexity isn't worth the added deploy/debug overhead.

A service worker is power and footgun. Reach for it when offline, push, or aggressive custom caching are genuine product needs — not because PWAs are trendy.

Follow-up questions

  • What's the difference between skipWaiting and clients.claim?
  • How does stale-while-revalidate work in a SW?
  • What's the gotcha with versioned static + non-versioned HTML in a SW cache?
  • How do push notifications work without an open tab?

Common mistakes

  • Forgetting to bump the cache name on deploy — users stuck on stale assets.
  • Caching index.html aggressively + serving new JS hashes — fatal mismatch.
  • Caching auth-bearing responses without partitioning by user — data leak.
  • Calling skipWaiting() without considering open tabs with the old SW's cache schema.
  • Writing custom SW logic that Workbox already handles correctly.
  • Registering SW on every page — register once at top-level.

Performance considerations

  • SW serves cached HTML/assets in <50ms even on slow networks → instant subsequent loads. Stale-while-revalidate gives near-instant cache hits while quietly updating. Pre-caching the next route's chunks makes navigation feel native. Trade-off: SW adds JS work on every request (small) and complexity to deploys.

Edge cases

  • Safari has different SW behavior, especially around background sync and persistent storage.
  • iOS Safari doesn't support push notifications until iOS 16.4+, and only for installed PWAs.
  • navigator.connection (data saver) — adjust strategy if user is on Save-Data.
  • Service worker can't intercept the SW script itself or the manifest by default.
  • Origin Trial / advanced features (BackgroundFetch, PeriodicSync) need feature detection and explicit user permission.

Real-world examples

  • Twitter Lite, Pinterest, Starbucks PWAs use SW for offline + speed.
  • Google Docs uses SW for offline editing.
  • Most major news sites pre-cache article shells for instant render.
  • next-pwa and vite-plugin-pwa wrap Workbox for framework apps.

Senior engineer discussion

Seniors decide if a SW is justified by the product (offline, push, repeat-visitor speed) rather than installing one by default. They use Workbox over hand-rolled code, design cache versioning carefully (the deploy story is half the work), and test the upgrade path (old SW + open tabs + new deploy). They also know the limits: SW won't fix a slow first visit; it accelerates repeat visits and enables offline.

Related questions