Basics of Service Workers
What Is a Service Worker?
A service worker is a JavaScript file that runs separately from a web page.
It can:
- Intercept network requests
- Cache files
- Serve cached files when offline
- Handle background tasks
- Enable parts of a website to work offline
- Support Progressive Web Apps (PWAs)
Browser
│
▼
Service Worker
│
├── Cache
│
└── Network
Registering a Service Worker
In your webpage:
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}
This tells the browser to register /sw.js.
Basic Service Worker
self.addEventListener("install", () => {
console.log("Service worker installed");
});
self.addEventListener("activate", () => {
console.log("Service worker activated");
});
self.addEventListener("fetch", (event) => {
console.log("Request:", event.request.url);
});
There are three important events:
| Event | Purpose |
|---|---|
install |
Initial setup |
activate |
Cleanup / becoming active |
fetch |
Intercept network requests |
Service Worker Lifecycle
register()
│
▼
install
│
▼
waiting
│
▼
activate
│
▼
controlling pages
A service worker does not immediately control the page that registered it.
Usually, a reload is needed after the first installation.
Caching Files
The Cache API can store resources:
const CACHE_NAME = "v1";
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(["/", "/index.html", "/style.css", "/app.js"]);
}),
);
});
Now those files can be available offline.
Serving Cached Files
A simple cache-first strategy:
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request).then((cached) => {
return cached || fetch(event.request);
}),
);
});
Behavior:
Request
│
▼
Cache?
┌─┴─┐
Yes No
│ │
▼ ▼
Cache Network
│
▼
Response
Offline Fallback
You can provide a special offline page:
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open("v1").then((cache) => {
return cache.add("/offline.html");
}),
);
});
self.addEventListener("fetch", (event) => {
event.respondWith(
fetch(event.request).catch(() => {
return caches.match("/offline.html");
}),
);
});
If the network fails, /offline.html is returned.
Updating a Cache
Use cache versions:
const CACHE_NAME = "v2";
Then delete old caches:
self.addEventListener("activate", (event) => {
event.waitUntil(
caches
.keys()
.then((keys) =>
Promise.all(
keys
.filter((key) => key !== CACHE_NAME)
.map((key) => caches.delete(key)),
),
),
);
});
Service Worker Scope
A service worker controls URLs under its scope.
For example:
/sw.js
normally controls:
/
├── index.html
├── about.html
└── app/
└── page.html
But:
/sw.js
does not normally control something outside its scope.
HTTPS Requirement
Service workers generally require a secure context.
That means:
https://example.com
works.
For development:
http://localhost:3000
is also allowed.
Service Workers Are Not Web Workers
Both run JavaScript outside the normal page execution context, but they have different purposes.
Web Worker
Used for background computation:
Page
│
└── Web Worker
└── heavy computation
Service Worker
Used for network/resource control:
Page
│
└── Service Worker
├── Cache
├── Network
└── Offline
Common Cache Strategies
Cache First
Cache → Network
Good for:
- Static assets
- Images
- Fonts
- Offline applications
Network First
Network → Cache
Good for:
- Frequently changing content
- News
- API responses
Stale While Revalidate
Cache → return immediately
Network → update cache
Good for:
- Content that can be slightly stale
- Websites where speed matters
Service Workers and PWAs
A service worker is one of the major pieces of a Progressive Web App.
A PWA commonly includes:
Website
├── HTTPS
├── Service Worker
├── Web App Manifest
└── Cached resources
The service worker is what makes reliable offline behavior possible.
Service Worker Limitations
Service workers:
- Cannot directly manipulate the DOM
- Have their own execution context
- Can be terminated by the browser
- Cannot assume they run continuously
- Have browser-specific storage limits
- Must follow service-worker security restrictions
The page and service worker communicate using APIs such as:
navigator.serviceWorker.controller;
and:
postMessage();
Minimal Offline Website
A basic structure:
my-site/
├── index.html
├── offline.html
├── style.css
├── app.js
└── sw.js
index.html:
<script>
navigator.serviceWorker.register("/sw.js");
</script>
sw.js:
const CACHE = "site-v1";
self.addEventListener("install", (event) => {
event.waitUntil(
caches
.open(CACHE)
.then((cache) =>
cache.addAll([
"/",
"/index.html",
"/offline.html",
"/style.css",
"/app.js",
]),
),
);
});
self.addEventListener("fetch", (event) => {
event.respondWith(
caches
.match(event.request)
.then((response) => response || fetch(event.request)),
);
});
Now previously cached resources can be loaded without a network connection.
Key Idea
A service worker sits between your website and the network:
┌─────────┐
│ Website │
└────┬────┘
│
▼
┌───────────────┐
│Service Worker │
└───────┬───────┘
│
┌────┴────┐
▼ ▼
Cache Network
The main reason to use one is control over network requests and offline behavior.