What a service worker is and how it differs from ordinary JavaScript
Ordinary JavaScript runs on the page’s main thread and has access to the DOM. A service worker runs on a separate background thread — it has no DOM access, but it can intercept every network request and manage the browser cache.
The service worker lifecycle:
- Registration — the page registers it with
navigator.serviceWorker.register('/sw.js') - Installation — the worker installs and may pre-cache static resources
- Activation — the worker takes control of the page
- Operation — it intercepts requests through the
fetchevent
// sw.js - a basic cache-first strategy
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cached => {
return cached || fetch(event.request);
})
);
});
Caching strategies
A service worker lets you apply different strategies to different resource types:
| Strategy | Logic | Where it fits |
|---|---|---|
| Cache first | Cache first, then the network | Static assets (CSS, JS, fonts) |
| Network first | Network first, cache on failure | HTML pages, data |
| Stale while revalidate | Serve the cache, refresh in the background | Images, semi-static content |
| Network only | Always the network, never the cache | Personalization APIs, the basket |
| Cache only | The cache alone | Offline fallbacks |
Service workers and personalization: the pitfalls
For an e-commerce site with personalization, a service worker creates one specific risk: a cached personalized response. If the recommendations API lands in the worker cache with a long TTL, another visitor or the next session receives someone else’s recommendations.
Rules for personalized data:
- Exclude from the cache every request carrying an
Authorizationheader or auserIdparameter - Network only for the recommendations, basket and profile APIs
- Cache only for fully static resources alone
// Excluding the personalization API from caching
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
if (url.pathname.startsWith('/api/personalization/')) {
// Always over the network - never cached
event.respondWith(fetch(event.request));
return;
}
// Everything else - cache first
event.respondWith(cacheFirst(event.request));
});
Use in an e-commerce PWA
- An offline page — a visitor with no connection sees a custom page rather than a browser error
- Prefetching — the worker loads the next page’s resources while the visitor reads the current one
- Web push — notifications about an abandoned basket, a price drop, new arrivals
- Background sync — adding to a wishlist offline and syncing once the connection returns
Important: every site deploy needs the service worker cache invalidated. Visitors can keep seeing the old version until the worker updates. Use versioned cache keys, or call
skipWaiting()for critical updates.