Service Workers and PWAs
A service worker is a background script that the browser runs separately from your web page, acting as a programmable network proxy that can cache responses and serve your app offline — the foundation of Progressive Web Apps (PWAs).
Learn Service Workers and PWAs in our free JavaScript course — an interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free JavaScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
💡 Running Code Locally: Service workers require a secure context and a real server. The snippets below are illustrative. For the best experience:
- Serve your site over HTTPS (or http://localhost during development)
- Use your browser's DevTools Application tab to inspect service workers and caches
- Download Node.js to run a local dev server
📮 Real-World Analogy: A service worker is like a helpful mailroom clerk for your app:
- • Network proxy = every request passes through the clerk first
- • Cache = a filing cabinet of saved copies the clerk can hand back instantly
- • Offline = even if the post office (network) is closed, the clerk serves from the cabinet
- • install/activate = the clerk stocks the cabinet, then takes over the desk
Service workers are the engine behind Progressive Web Apps: web apps that load fast, work offline, and can be installed to the home screen like native apps. A service worker runs in its own thread, has no access to the DOM, and stays event-driven — the browser wakes it to handle events like install , activate , and fetch , then lets it sleep. Because it can intercept every network request, it must be served over HTTPS .
Lifecycle Event
When It Fires
Typical Use
install
First registration / new version
Pre-cache the app shell
activate
After install, when taking control
Clean up old caches
fetch
On every network request in scope
Serve from cache or network
🔥 Registering a Service Worker
From your page, register the worker with navigator.serviceWorker.register() . Always feature-detect first, and register after load so it does not compete with critical page resources.
The returned registration's scope tells you which pages this worker controls — by default, the folder the script lives in and everything beneath it.
🔥 The Install Event: Pre-Caching
Inside the worker ( sw.js ), the install event is where you open a cache and store the core files. Wrap async work in event.waitUntil() so the browser knows installation is not finished until caching completes.
cache.addAll() fetches and stores every URL in the array. If any request fails, installation fails — so keep the pre-cache list to essential, reliable assets.
🔥 The Activate Event: Cleaning Old Caches
When a new worker activates, old caches from previous versions should be deleted. The activate event is the right place, because the previous worker is no longer in control.
Bumping the cache name (for example to app-shell-v2 ) on each release lets the activate handler automatically purge stale assets.
🔥 The Fetch Event and Cache API
The fetch event lets the worker intercept requests. With event.respondWith() you decide the response — often by checking the cache first via caches.match() .
Cache-first is great for static assets that rarely change. It returns instantly from storage and only touches the network on a miss.
🔥 Network-First with Offline Fallback
For content that should be fresh (like API data or HTML), use network-first : try the network, and fall back to the cache (or an offline page) when the request fails.
Note the response.clone() : a response body can only be read once, so you clone it to both return it and store it. This pattern keeps users online-fresh but offline-capable.
🔥 The Web App Manifest and Installability
A PWA becomes installable when you provide a web app manifest — a JSON file describing the app — and link it from your HTML. Combined with a service worker over HTTPS, browsers offer an "Install" prompt.
Key fields: name / short_name , a start_url , a display mode like standalone , and appropriately sized icons (at least 192px and 512px). Installed PWAs launch in their own window without browser chrome.
🔥 Scope and the HTTPS Requirement
A worker registered at /app/sw.js controls only /app/ and below. You can narrow scope with the scope option, but you cannot widen it beyond the script's directory. And because the worker can rewrite traffic, the origin must be secure .
HTTPS prevents man-in-the-middle attackers from hijacking the proxy. localhost is exempt so you can develop without a certificate.
🔥 Service Workers vs Web Workers
Both run off the main thread and cannot touch the DOM, but they solve different problems. A Web Worker offloads CPU-heavy work for a single page. A service worker is a persistent, event-driven network proxy shared across all pages in its scope.
Aspect
Web Worker
Service Worker
Main purpose
Offload CPU-heavy tasks
Proxy network, cache, offline
Lifetime
Tied to its page
Persists across page loads
Intercept requests
No
Yes (fetch event)
Scope
One page
All pages in its URL scope
🔥 Common Service Worker Mistakes to Avoid
❌ Anti-Patterns
- Serving over plain HTTP — registration fails outside a secure context (HTTPS or localhost)
- Forgetting event.waitUntil() — the browser may terminate the worker before caching finishes
- Never bumping the cache version — users get stuck on stale assets forever
- Reading a response body twice — clone it with response.clone() before caching
- Expecting DOM access — service workers have no window or document
🎯 Key Takeaways
- A service worker is a background, event-driven network proxy with no DOM access
- Register it with navigator.serviceWorker.register() over HTTPS
- The lifecycle is install → activate → fetch
- Pre-cache on install; clean old caches on activate
- Use the Cache API and caches.match() for cache-first or network-first strategies
- A web app manifest plus a service worker makes a PWA installable
- Scope is bounded by the script's directory; HTTPS is mandatory
- Service workers persist across pages; Web Workers handle per-page CPU work
Practice quiz
What is a service worker?
- A script the browser runs in the background, separate from the page, acting as a network proxy
- A type of database
- A server-side rendering engine
- A CSS preprocessor
Answer: A script the browser runs in the background, separate from the page, acting as a network proxy. A service worker is a background script that runs separately from the web page and can intercept network requests, acting as a programmable proxy.
Which API registers a service worker?
- document.registerSW()
- serviceWorker.start()
- navigator.serviceWorker.register()
- window.installWorker()
Answer: navigator.serviceWorker.register(). You register a service worker by calling navigator.serviceWorker.register('/sw.js'), usually after the page loads.
What is the correct order of the core service worker lifecycle events?
- activate, install, fetch
- install, activate, fetch
- fetch, activate, install
- fetch, install, activate
Answer: install, activate, fetch. A service worker first installs (good for pre-caching), then activates (good for cleaning old caches), then handles fetch events.
During which event do you typically pre-cache the app shell?
- The fetch event
- The message event
- The activate event
- The install event
Answer: The install event. The install event is the ideal place to open a cache and pre-cache the core app shell with event.waitUntil().
Which method looks for a matching cached response?
- caches.match()
- storage.lookup()
- cache.find()
- fetch.cached()
Answer: caches.match(). caches.match(request) searches the Cache Storage for a response that matches the request and returns it if found.
What does a 'cache-first' strategy do?
- It only ever uses the network
- It serves from cache if available, falling back to the network
- It deletes the cache on every request
- It always ignores the cache
Answer: It serves from cache if available, falling back to the network. Cache-first returns a cached response immediately when present and only goes to the network on a cache miss, enabling fast offline-capable loads.
What makes a Progressive Web App installable?
- A large image file
- A single HTML file with no JavaScript
- Disabling the cache
- A valid web app manifest plus a registered service worker served over HTTPS
Answer: A valid web app manifest plus a registered service worker served over HTTPS. Installability requires a web app manifest (name, icons, start_url, display), a service worker, and a secure (HTTPS) origin.
What security requirement do service workers have?
- They only work in incognito mode
- They must be inline in the HTML
- They must be served over HTTPS (localhost is exempt for development)
- They require a paid certificate from the browser vendor
Answer: They must be served over HTTPS (localhost is exempt for development). Because they can intercept network traffic, service workers require a secure context (HTTPS), with localhost allowed for development.
What does a service worker's scope control?
- The size of the cache only
- Which URLs/pages the service worker can control and intercept
- The number of tabs allowed
- The colors of the page
Answer: Which URLs/pages the service worker can control and intercept. Scope defines the set of pages a service worker controls; by default it is the directory of the script and cannot be broader than that path.
How does a service worker differ from a regular Web Worker?
- A service worker is event-driven, persists across page loads, and can intercept network requests, while a Web Worker runs tied to a page for CPU work
- They are identical
- A Web Worker can intercept network requests but a service worker cannot
- A service worker can directly manipulate the DOM
Answer: A service worker is event-driven, persists across page loads, and can intercept network requests, while a Web Worker runs tied to a page for CPU work. A Web Worker offloads CPU work for a single page; a service worker is a persistent, event-driven network proxy shared across pages. Neither can touch the DOM directly.
Continue this course
- Previous: Web Components and Custom Elements
- Next: WebAssembly and JS Interop