Service Workers
This lesson explains Service Workers with clear examples, use cases, and best practices so you can use HTML5 APIs confidently in real web applications.
HTML5 Service Workers Overview
Service Workers are background scripts that run separately from the main
web page. They allow web applications to intercept network requests,
cache files, provide offline support, send push notifications, and create
Progressive Web App experiences.
Service Workers are commonly used for offline apps, app shell caching,
background sync, push notifications, asset caching, API response caching,
faster page loading, and reliable mobile-first web applications.
| Feature | Description |
| API Name | Service Workers API |
| Main Object | navigator.serviceWorker |
| Main File | service-worker.js |
| Main Events | install, activate, fetch, push, sync |
| Security | Requires HTTPS in production. |
| Best Use | Offline support, caching, PWAs, background features. |
Register a Service Worker
// main.js
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker
.register("/service-worker.js")
.then((registration) => {
console.log(
"Service Worker registered:",
registration.scope
);
})
.catch((error) => {
console.error(
"Service Worker registration failed:",
error
);
});
});
}
The service worker is registered from the main JavaScript file. After
registration, the browser downloads, installs, and activates the worker.
Service Worker Lifecycle
| Lifecycle Step | Description | Common Use |
| Register | Browser registers the service worker file. | Start service worker setup. |
| Install | Service worker installs for the first time or after an update. | Cache app shell files. |
| Activate | New service worker becomes active. | Delete old caches. |
| Fetch | Service worker intercepts network requests. | Serve cache or network response. |
| Update | Browser detects changed service worker file. | Install new version. |
Install Event and Cache App Shell
// service-worker.js
const CACHE_NAME =
"app-cache-v1";
const APP_SHELL =
[
"/",
"/offline",
"/styles/main.css",
"/scripts/app.js",
"/icons/icon-192.png"
];
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
return cache.addAll(APP_SHELL);
})
);
});
The install event is commonly used to cache important files
required for the application shell.
Activate Event and Delete Old Caches
// service-worker.js
const CURRENT_CACHE =
"app-cache-v2";
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys()
.then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CURRENT_CACHE) {
return caches.delete(cacheName);
}
})
);
})
);
});
The activate event is useful for removing old cache versions
and preparing the new service worker.
Fetch Event Example
// service-worker.js
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request)
.then((cachedResponse) => {
if (cachedResponse) {
return cachedResponse;
}
return fetch(event.request);
})
);
});
The fetch event allows the service worker to intercept
requests and decide whether to return cached data or make a network
request.
Cache-First Strategy
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request)
.then((cachedResponse) => {
return cachedResponse || fetch(event.request);
})
);
});
Cache-first is best for static files such as CSS, JavaScript, images,
icons, fonts, and app shell files.
Network-First Strategy
self.addEventListener("fetch", (event) => {
event.respondWith(
fetch(event.request)
.then((networkResponse) => {
const responseClone =
networkResponse.clone();
caches.open("dynamic-cache-v1")
.then((cache) => {
cache.put(
event.request,
responseClone
);
});
return networkResponse;
})
.catch(() => {
return caches.match(event.request);
})
);
});
Network-first is useful for dynamic content that should be fresh when
online but still available from cache when offline.
Offline Fallback Page
self.addEventListener("fetch", (event) => {
if (event.request.mode === "navigate") {
event.respondWith(
fetch(event.request)
.catch(() => {
return caches.match("/offline");
})
);
}
});
Offline fallback pages provide a better experience than showing the
browser's default network error page.
Cache API Methods Used with Service Workers
| Method | Description | Example Syntax |
caches.open() | Opens or creates a named cache. | caches.open("app-v1") |
cache.addAll() | Adds multiple files to cache. | cache.addAll(files) |
cache.put() | Stores a request and response pair. | cache.put(request, response) |
caches.match() | Finds a matching cached response. | caches.match(request) |
caches.keys() | Returns all cache names. | caches.keys() |
caches.delete() | Deletes a cache. | caches.delete("old-cache") |
Activate New Service Worker Immediately
// service-worker.js
self.addEventListener("install", () => {
self.skipWaiting();
});
self.addEventListener("activate", (event) => {
event.waitUntil(
self.clients.claim()
);
});
skipWaiting() activates the new worker faster, and
clients.claim() allows it to control open pages immediately.
Use this carefully when cache changes may affect active users.
Send Messages Between Page and Service Worker
// main.js
navigator.serviceWorker.ready
.then((registration) => {
registration.active.postMessage(
{
type: "CLEAR_CACHE"
}
);
});
// service-worker.js
self.addEventListener("message", (event) => {
if (event.data.type === "CLEAR_CACHE") {
caches.keys()
.then((names) => {
names.forEach((name) => {
caches.delete(name);
});
});
}
});
Messaging allows the page and service worker to coordinate cache updates,
logout cleanup, or background tasks.
Background Sync Example
// main.js
async function registerSync() {
const registration =
await navigator.serviceWorker.ready;
if ("sync" in registration) {
await registration.sync.register(
"sync-orders"
);
}
}
// service-worker.js
self.addEventListener("sync", (event) => {
if (event.tag === "sync-orders") {
event.waitUntil(
syncPendingOrders()
);
}
});
Background Sync can retry failed actions when the network returns. Always
provide a manual retry fallback because browser support varies.
Push Notification Example
// service-worker.js
self.addEventListener("push", (event) => {
const data =
event.data
? event.data.json()
: {
title: "New Update",
body: "You have a new notification."
};
event.waitUntil(
self.registration.showNotification(
data.title,
{
body: data.body,
icon: "/icons/icon-192.png"
}
)
);
});
Service Workers can display notifications even when the page is not open,
making them important for Progressive Web Apps.
Handle Notification Click
// service-worker.js
self.addEventListener("notificationclick", (event) => {
event.notification.close();
event.waitUntil(
clients.openWindow("/notifications")
);
});
The notificationclick event helps users return to the correct
page when they click a notification.
Common Service Worker Events
| Event | Purpose | Common Use |
install | Runs when service worker installs. | Pre-cache app shell. |
activate | Runs when service worker activates. | Clean old caches. |
fetch | Intercepts network requests. | Offline and caching strategies. |
message | Receives messages from page. | Cache cleanup, update commands. |
sync | Runs background sync tasks. | Retry failed form submissions. |
push | Receives push messages. | Show push notifications. |
notificationclick | Runs when user clicks notification. | Open related app page. |
Service Worker vs Web Worker
| Feature | Service Worker | Web Worker |
| Main Purpose | Network proxy, offline support, caching, push. | Background JavaScript processing. |
| Controls Network | Yes. | No. |
| Can Access DOM | No. | No. |
| Lifecycle | Event-driven and can stop when idle. | Runs while page keeps it alive. |
| Common Use | PWA, offline, cache, push notifications. | Heavy calculations, parsing, image processing. |
Feature Detection
if ("serviceWorker" in navigator) {
console.log("Service Workers are supported.");
} else {
console.log("Service Workers are not supported.");
}
Always check support before registering a service worker. Service Workers
require HTTPS in production, but they work on localhost during development.
Common Service Worker Use Cases
- Offline-first Progressive Web Apps.
- App shell caching.
- Offline fallback pages.
- Static asset caching.
- API response caching.
- Background Sync for failed requests.
- Push notifications.
- Faster repeat page loads.
Service Worker Best Practices
- Use HTTPS in production.
- Keep the service worker file small and focused.
- Version caches clearly.
- Delete old caches during activation.
- Use the correct caching strategy for each request type.
- Do not cache sensitive account or payment pages by default.
- Provide offline fallback pages for navigation requests.
- Test updates, offline mode, and cache clearing in browser DevTools.
Security and Privacy Considerations
- Service Workers can intercept requests, so only register trusted scripts.
- Do not cache private data unless there is a clear security plan.
- Clear user-specific caches during logout.
- Validate cached API responses before rendering.
- Use HTTPS and secure cookies for authenticated sessions.
- Be careful when caching pages that contain personal information.
- Do not expose secrets in service worker code.
- Keep push notification content privacy-friendly.
Common Service Worker Mistakes
- Registering a service worker without feature detection.
- Forgetting HTTPS requirements in production.
- Caching everything without a strategy.
- Not deleting old caches after updates.
- Breaking refresh or deep-link behavior offline.
- Serving stale files after deployment.
- Not handling failed network requests.
- Caching sensitive user data accidentally.
Key Takeaways
- Service Workers run in the background separately from web pages.
- They enable offline support, caching, push notifications, and background sync.
- Use
navigator.serviceWorker.register() to register a worker. - Use
install, activate, and fetch events for caching. - Use Cache API with Service Workers for offline app shell and assets.
- Always use secure caching, cache versioning, and fallback strategies.
Pro Tip
Start simple: cache only your app shell, static assets, and offline page.
Add API caching, Background Sync, and push notifications only when your
application clearly needs them.