Service Worker Generator
Generate a real, modern service worker with per-route caching rules (static assets → cache-first, /api/ → network-first, HTML → stale-while-revalidate), an app-shell precache, an offline fallback page, cache versioning with automatic cleanup, and skipWaiting / clients.claim toggles — plus the registration snippet with an update-available flow and an unregister snippet.
About this ToolHow it works, benefits & use casesTap to collapse
Generate a complete, modern service worker with per-route caching rules instead of a single global strategy. You build an ordered route table where each rule maps a URL pattern — a glob like *.css or a path substring like /api/ — to one of five strategies: cache-first, network-first, stale-while-revalidate, network-only or cache-only. Each request is matched top-to-bottom and the first matching rule wins; anything unmatched falls through to a configurable default strategy. You also define an app-shell precache (one URL per line, cached during install), name the cache and give it a numeric version (combined into a name-vN key), and optionally enable an offline fallback page that is served for navigations when both network and cache miss — it is automatically added to the precache. Lifecycle toggles control skipWaiting(), clients.claim() and old-cache cleanup on activate. The output is split across three live tabs: the sw.js itself, a registration snippet (with an optional update-available flow and configurable SW path), and an unregister snippet. Everything updates as you edit, with copy, download and shareable-URL support.
How to Use
- 1Set the cache name and version (bump the version to invalidate old caches), then list app-shell URLs to precache, one per line.
- 2Add route rules mapping a URL pattern (e.g. *.css or /api/) to a strategy; rules are matched top-to-bottom, first match wins.
- 3Pick a default strategy for requests that no rule matches.
- 4Optionally enable the offline fallback page and set lifecycle toggles for skipWaiting, clients.claim, cache cleanup and the update flow.
- 5Switch between the sw.js, Registration and Unregister tabs, then copy, download or share the generated code.
Key Benefits
- Per-route caching rules with glob or path-substring patterns, matched first-match-wins
- Five strategies (cache-first, network-first, stale-while-revalidate, network-only, cache-only) plus a configurable default
- App-shell precache list cached on install, with the offline page auto-added
- Offline fallback page served for navigations when both network and cache miss
- Versioned cache name with automatic cleanup of stale caches on activate
- Individually toggleable skipWaiting(), clients.claim() and update-available registration flow
- Three live tabs (sw.js, registration, unregister) with copy, download and shareable-URL output
Common Use Cases
- Adding offline support and an app shell to a Progressive Web App
- Caching hashed static assets cache-first while keeping /api/ network-first in one worker
- Serving HTML stale-while-revalidate for instant loads that refresh in the background
- Showing a friendly offline.html for navigations when the user loses connectivity
- Producing a dependency-free starter sw.js plus its registration code to adapt before reaching for Workbox
Save at your scope root — updates live as you edit
/**
* Service Worker — generated by consolelog.tools
* Strategy: per-route caching with a network-first default.
*
* Save this file as '/offline.html' sibling, sw.js at the
* scope root you want it to control, then register it (see the registration
* snippet). Requires HTTPS (localhost is exempt).
*/
const CACHE_NAME = 'my-app-v1';
const PRECACHE_URLS = [
"/",
"/index.html",
"/styles/main.css",
"/scripts/app.js",
"/manifest.webmanifest",
"/offline.html"
];
const OFFLINE_URL = '/offline.html';
// ── Install: precache the app shell ──────────────────────────────────────────
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE_URLS)),
);
// Activate this worker as soon as it finishes installing.
self.skipWaiting();
});
// ── Activate: drop stale caches from previous versions ───────────────────────
self.addEventListener('activate', (event) => {
event.waitUntil(
(async () => {
const keys = await caches.keys();
await Promise.all(
keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)),
);
// Take control of open clients without requiring a reload.
await self.clients.claim();
})(),
);
});
// Only responses worth caching: ok, basic/cors, and not partial (206).
function isCacheable(response) {
return (
response &&
response.status === 200 &&
(response.type === 'basic' || response.type === 'cors')
);
}
// Compile a route pattern to a matcher. Globs (with *) become RegExps;
// everything else is a substring test against the pathname (or full URL when
// the pattern includes a scheme).
function compileMatcher(pattern) {
if (pattern.includes('*')) {
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
const re = new RegExp('^' + escaped + '$');
return (url) => re.test(url.pathname) || re.test(url.href);
}
if (pattern.includes('://')) {
return (url) => url.href.includes(pattern);
}
return (url) => url.pathname.includes(pattern);
}
async function staleWhileRevalidate(request) {
const cache = await caches.open(CACHE_NAME);
const cached = await cache.match(request);
const network = fetch(request)
.then((response) => {
if (isCacheable(response)) cache.put(request, response.clone());
return response;
})
.catch(async () => {
if (request.mode === 'navigate') {
const fallback = await caches.match(OFFLINE_URL);
if (fallback) return fallback;
}
return cached || Response.error();
});
return cached || network;
}
async function networkFirst(request) {
const cache = await caches.open(CACHE_NAME);
try {
const response = await fetch(request);
if (isCacheable(response)) cache.put(request, response.clone());
return response;
} catch (error) {
const cached = await cache.match(request);
if (cached) return cached;
if (request.mode === 'navigate') {
const fallback = await caches.match(OFFLINE_URL);
if (fallback) return fallback;
}
throw error;
}
}
async function cacheFirst(request) {
const cache = await caches.open(CACHE_NAME);
const cached = await cache.match(request);
if (cached) return cached;
try {
const response = await fetch(request);
if (isCacheable(response)) cache.put(request, response.clone());
return response;
} catch (error) {
if (request.mode === 'navigate') {
const fallback = await caches.match(OFFLINE_URL);
if (fallback) return fallback;
}
throw error;
}
}
// Ordered route table — the first matching rule handles the request.
const ROUTES = [
{ pattern: '/', handler: staleWhileRevalidate },
{ pattern: '/api/', handler: networkFirst },
{ pattern: '*.css', handler: cacheFirst },
{ pattern: '*.js', handler: cacheFirst },
{ pattern: '*.{png,jpg,jpeg,svg,webp,gif,ico}', handler: cacheFirst },
].map((route) => ({ match: compileMatcher(route.pattern), handler: route.handler }));
async function defaultHandler(request) {
return networkFirst(request);
}
// ── Fetch: dispatch each request to its matching strategy ────────────────────
self.addEventListener('fetch', (event) => {
const { request } = event;
// Never intercept non-GET (POST/PUT/etc.) — let them hit the network.
if (request.method !== 'GET') return;
const url = new URL(request.url);
const route = ROUTES.find((r) => r.match(url));
event.respondWith((route ? route.handler : defaultHandler)(request));
});
Cache
Versioned as name-vN at runtime
Bump to invalidate old caches
Precache (app shell)
URLs cached during install — one per line. These are the files needed to render your shell offline.
5 URLs will be precached.
Route rules (5)
Each request is matched against rules top-to-bottom; the first match wins. Use * globs (e.g. *.css) or a path substring (e.g. /api/).
API calls and dynamic content that needs to be fresh
Offline fallback
Auto-added to the precache so it's available offline
Lifecycle & registration
Detects a waiting worker and reloads once it takes control
Determines the SW scope — keep it at the root to control the whole origin
Caching strategies & gotchas
- Cache-first — serve from cache, fall back to network. Best for hashed/static assets (CSS, JS, images, fonts).
- Network-first — try the network, fall back to cache. Best for
/api/and dynamic data that must stay fresh. - Stale-while-revalidate — serve cache instantly and refresh in the background. A good default for HTML/navigations.
- Network-only / cache-only — never cache / never hit the network. Use sparingly for sensitive or fully-offline routes.
Gotchas: service workers require HTTPS (localhost is exempt). Scope is limited to the directory the SW is served from, so host sw.js at the site root to control everything. Bump the version on every deploy so the activate step purges stale caches. The generated SW only intercepts GET requests and skips opaque/partial responses.
Was this tool helpful?
Share Your Experience
Help others discover this tool!
Related tools
- PWA Manifest GeneratorBuild an installable web app manifest with a live installability audit, maskable icons, shortcuts, and iOS head tags
- JavaScript FormatterFormat and beautify JavaScript code
- Heroku Procfile GeneratorBuild a Heroku Procfile and matching app.json — process types, env vars, addons, dyno formation — plus a Procfile parser and audit
- App Icon GeneratorTurn one image into complete icon sets for iOS, Android, Web/PWA & macOS — Contents.json, adaptive icons, maskable, manifest & favicon.ico — in one ZIP
- Render Config GeneratorBuild a render.yaml blueprint — services (web/worker/cron/static), databases, typed env vars, fromDatabase links — with live YAML and an audit
- Bundle Analyzer VisualizerPaste a bundle file list to get a visual size treemap, gzip/brotli estimates, performance-budget pass/fail checks, and a build-vs-build diff of what grew or shrank
You build an ordered list of rules, each pairing a URL pattern with a strategy. In the generated worker every GET request is tested against the rules top-to-bottom and the first match handles it. A pattern containing * is compiled to a RegExp (matched against the pathname or full URL); otherwise it is treated as a substring of the pathname, or of the full URL when it includes a scheme like https://. Any request that matches no rule is handled by the default strategy you choose.
Cache-first serves from cache and falls back to the network — ideal for hashed/static assets (CSS, JS, images, fonts). Network-first tries the network then the cache — best for /api/ and dynamic data that must stay fresh. Stale-while-revalidate returns the cache instantly and refreshes in the background — a good default for HTML/navigations. Network-only never caches and cache-only never hits the network; use both sparingly for sensitive or fully-offline routes.

