Progressive Web Apps, die auch ohne Netz funktionieren
Ein Service Worker ist kein optionales Feature – er ist die Grundlage für Apps, die Nutzern auch bei schlechter oder fehlender Verbindung eine vollständige Erfahrung bieten. Wer Offline-First denkt, baut Apps, die schneller, zuverlässiger und installierbarer sind als klassische Webanwendungen.
Inhaltsverzeichnis
- 1. Was ein Service Worker wirklich ist
- 2. Lebenszyklus: Install, Activate, Fetch
- 3. Caching-Strategien im Vergleich
- 4. Offline-First: Fallback-Seiten und Shell-Caching
- 5. Background Sync: Aktionen bei Wiederverbindung
- 6. Push Notifications: Nutzer zurückholen
- 7. Workbox: Service Worker ohne Boilerplate
- 8. Debugging und Update-Mechanismus
- 9. Caching-Strategien im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Was ein Service Worker wirklich ist
Ein Service Worker ist ein JavaScript-Skript, das der Browser unabhängig von der Webseite im Hintergrund ausführt – ohne Zugang zum DOM, ohne blockierendes Rendering, aber mit vollständiger Kontrolle über Netzwerkanfragen. Er sitzt als programmierbarer Proxy zwischen der Webanwendung und dem Netzwerk und entscheidet für jede ausgehende Anfrage, ob sie aus dem Cache bedient, an das Netz weitergegeben oder durch einen Fallback ersetzt wird. Diese Position macht den Service Worker zur zentralen Infrastrukturkomponente jeder ernsthaften Progressive Web App.
Der grundlegende Unterschied zu einem Web Worker liegt in der Persistenz: Ein Service Worker überlebt das Schließen des Browser-Tabs und wird vom Browser reaktiviert, sobald eine Netzwerkanfrage aus seiner Domäne eintrifft oder ein Push-Event ankommt. Diese Fähigkeit ist die Grundlage für Offline-Funktionalität, Hintergrundnachrichtenverarbeitung und Push-Benachrichtigungen. Der Service Worker läuft ausschließlich unter HTTPS – auf localhost ist HTTP aus Entwicklungsgründen erlaubt. Wer Offline-First als Designprinzip ernst nimmt, plant den Service Worker von Anfang an mit, statt ihn nachträglich aufzusetzen.
2. Lebenszyklus: Install, Activate, Fetch
Der Lebenszyklus eines Service Workers besteht aus drei Phasen, die exakt verstanden werden müssen, um Caching-Bugs und veraltete Inhalte zu vermeiden. Die erste Phase ist Install: Sobald der Browser das Registrierungsskript ausführt und einen neuen Service Worker erkennt, wird das install-Event ausgelöst. Hier öffnet man einen benannten Cache und lädt alle statischen Assets vorab, die die Anwendung im Offline-Betrieb benötigt. Mit event.waitUntil() signalisiert man dem Browser, die Installation nicht als abgeschlossen zu markieren, bevor alle Assets gecacht sind.
Die zweite Phase ist Activate: Nach erfolgter Installation wartet der Service Worker, bis alle Tabs der Anwendung geschlossen sind, bevor er die Kontrolle übernimmt. Das verhindert, dass ein laufender Tab plötzlich mit einem neuen Service Worker konfrontiert wird, der andere Cache-Versionen erwartet. In der Activate-Phase räumt man alle alten Caches auf. Die dritte Phase ist der kontinuierliche Fetch-Handler: Jede Netzwerkanfrage der Anwendung löst ein fetch-Event aus, das der Service Worker abfangen und beliebig behandeln kann. Hier werden die Caching-Strategien implementiert.
// service-worker.js — Full lifecycle with cache versioning
const CACHE_NAME = 'mironsoft-v1';
const STATIC_ASSETS = [
'/',
'/index.html',
'/app.js',
'/styles.css',
'/offline.html',
'/icons/icon-192.png',
];
// Install: pre-cache all static assets
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
console.log('[SW] Pre-caching static assets');
return cache.addAll(STATIC_ASSETS);
})
);
// Take control immediately without waiting for tab reload
self.skipWaiting();
});
// Activate: delete old caches
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys
.filter((key) => key !== CACHE_NAME)
.map((key) => {
console.log('[SW] Deleting old cache:', key);
return caches.delete(key);
})
)
)
);
// Claim all open clients immediately
self.clients.claim();
});
3. Caching-Strategien im Vergleich
Es gibt keine universell beste Caching-Strategie für Service Worker – die Wahl hängt vom Asset-Typ und den Anforderungen der Anwendung ab. Die Strategie Cache First liefert immer aus dem Cache und fällt nur auf das Netz zurück, wenn das Asset dort nicht vorhanden ist. Sie ist ideal für statische Assets wie CSS, JavaScript und Bilder, die sich selten ändern. Der Vorteil ist maximale Geschwindigkeit; der Nachteil ist, dass veraltete Inhalte lange im Cache bleiben, wenn die Cache-Versionierung nicht sauber implementiert ist.
Die Strategie Network First fragt immer zuerst das Netzwerk an und fällt bei einem Fehler auf den Cache zurück. Sie eignet sich für API-Antworten, die immer aktuell sein sollen, aber im Offline-Fall noch brauchbare veraltete Daten liefern können. Die Strategie Stale While Revalidate liefert sofort aus dem Cache, aktualisiert den Cache aber gleichzeitig im Hintergrund mit der Netzwerkantwort. Sie kombiniert Geschwindigkeit mit Aktualität und ist für Ressourcen ideal, bei denen leicht veraltete Daten vertretbar sind. Eine vierte Strategie, Cache Only, liefert ausschließlich aus dem Cache – nützlich für Assets, die beim Install vorab gecacht wurden und sich nie ändern.
4. Offline-First: Fallback-Seiten und Shell-Caching
Das Kern-Muster von Offline-First ist das App Shell Model: Der minimale HTML-, CSS- und JavaScript-Rahmen der Anwendung wird beim ersten Besuch vollständig gecacht und danach immer aus dem Cache geladen. Dynamische Inhalte werden separat über API-Aufrufe nachgeladen und ebenfalls gecacht. So startet die Anwendung auch ohne Netzwerkverbindung sofort und zeigt zumindest gecachte Inhalte, während sie auf eine Verbindung wartet. Der Service Worker ist die Infrastruktur, die dieses Muster umsetzt.
Für Seiten, die nicht im Cache vorhanden sind und auch nicht über das Netz erreichbar sind, braucht man eine Offline-Fallback-Seite. Sie wird beim Install vorab gecacht und im Fetch-Handler ausgeliefert, wenn eine Navigationsanfrage weder aus dem Cache noch aus dem Netzwerk bedient werden kann. Die Fallback-Seite informiert den Nutzer über den Offline-Zustand, zeigt gecachte Inhalte an und bietet einen Retry-Button. Gut implementierte Offline-Fallback-Seiten verbessern die wahrgenommene Zuverlässigkeit einer Anwendung erheblich, selbst wenn der Nutzer sie nie zu Gesicht bekommt.
// service-worker.js — Fetch handler with multiple strategies
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// Strategy: Cache First for static assets
if (request.destination === 'style' ||
request.destination === 'script' ||
request.destination === 'image') {
event.respondWith(
caches.match(request).then((cached) =>
cached ?? fetch(request).then((response) => {
const clone = response.clone();
caches.open(CACHE_NAME).then((c) => c.put(request, clone));
return response;
})
)
);
return;
}
// Strategy: Network First for API calls
if (url.pathname.startsWith('/api/')) {
event.respondWith(
fetch(request)
.then((response) => {
const clone = response.clone();
caches.open(CACHE_NAME).then((c) => c.put(request, clone));
return response;
})
.catch(() => caches.match(request))
);
return;
}
// Strategy: Offline fallback for navigation requests
if (request.mode === 'navigate') {
event.respondWith(
fetch(request).catch(() => caches.match('/offline.html'))
);
}
});
5. Background Sync: Aktionen bei Wiederverbindung
Background Sync löst ein konkretes Problem des Offline-First-Ansatzes: Nutzer möchten auch ohne Verbindung Aktionen ausführen – Formulare absenden, Bewertungen abgeben, Daten speichern. Ohne Background Sync gehen diese Aktionen verloren, wenn die Verbindung fehlt. Mit der Background Sync API registriert die Anwendung einen Sync-Tag, wenn eine Aktion fehlschlägt oder offline durchgeführt wird. Der Service Worker empfängt das sync-Event, sobald der Browser wieder online ist, und wiederholt die Aktion dann zuverlässig – auch wenn der Tab inzwischen geschlossen wurde.
Die Implementierung folgt einem klaren Muster: Die fehlgeschlagene Anfrage wird in IndexedDB gespeichert. Der Sync-Tag wird über navigator.serviceWorker.ready.then(reg => reg.sync.register('tag')) registriert. Im Service Worker liest das sync-Event alle gespeicherten Anfragen aus IndexedDB und sendet sie erneut. Wenn eine Anfrage erfolgreich war, wird sie aus IndexedDB gelöscht. Wenn sie wieder fehlschlägt, plant der Browser automatisch einen neuen Retry mit exponential backoff. Dieser Mechanismus macht Offline-Aktionen genauso zuverlässig wie Online-Aktionen.
6. Push Notifications: Nutzer zurückholen
Die Push API in Kombination mit dem Service Worker ermöglicht Benachrichtigungen an Nutzer, auch wenn die Webanwendung nicht geöffnet ist. Der Ablauf: Die Anwendung fragt die Benachrichtigungsberechtigung an, abonniert den Browser beim Push-Service (VAPID-Authentifizierung) und sendet den Subscription-Endpunkt an den eigenen Server. Der Server sendet verschlüsselte Push-Nachrichten an den Browser-Push-Dienst. Der Browser aktiviert den Service Worker, sobald eine Push-Nachricht eintrifft – unabhängig vom Zustand der Anwendung.
VAPID (Voluntary Application Server Identification) ist der Standard für sichere Push-Authentifizierung. Der Server erzeugt ein VAPID-Schlüsselpaar, sendet den öffentlichen Schlüssel an die Anwendung, und die Subscription enthält diesen Schlüssel als Authentifizierungsmerkmal. Der Push-Dienst des Browsers kann damit verifizieren, dass nur der autorisierte Server Nachrichten für diese Subscription senden kann. Die Implementierung auf Node.js-Seite nutzt die Bibliothek web-push; der Service Worker empfängt das push-Event und zeigt die Benachrichtigung mit self.registration.showNotification() an.
// app.js — Subscribe to push notifications
async function subscribeToPush() {
const registration = await navigator.serviceWorker.ready;
// Public VAPID key from server
const publicKey = 'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U';
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicKey),
});
// Send subscription endpoint to your server
await fetch('/api/push/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(subscription),
});
}
// service-worker.js — Handle incoming push event
self.addEventListener('push', (event) => {
const data = event.data?.json() ?? { title: 'Neue Nachricht', body: '' };
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icons/icon-192.png',
badge: '/icons/badge-72.png',
data: { url: data.url ?? '/' },
})
);
});
// Handle notification click — open or focus the app
self.addEventListener('notificationclick', (event) => {
event.notification.close();
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
});
7. Workbox: Service Worker ohne Boilerplate
Workbox ist eine von Google entwickelte Bibliothek, die die häufigsten Service Worker-Muster in wiederverwendbare Module kapselt. Statt Caching-Strategien manuell zu implementieren, nutzt man registerRoute() mit einem der eingebauten Strategie-Objekte: CacheFirst, NetworkFirst, StaleWhileRevalidate, NetworkOnly oder CacheOnly. Workbox übernimmt Cache-Verwaltung, Ablaufzeiten, Cache-Größenbeschränkungen und Fehlerbehandlung. Das Ergebnis ist ein deutlich kürzerer, besser lesbarer und robusterer Service Worker.
Für Produktionsprojekte empfiehlt sich die Integration über workbox-webpack-plugin oder vite-plugin-pwa. Diese Plugins generieren den Service Worker automatisch aus der Build-Ausgabe, erstellen die Precache-Manifest-Liste mit Inhalts-Hashes und aktualisieren den Service Worker bei jedem Build. Der Workbox Service Worker unterscheidet sich von einem manuell geschriebenen dadurch, dass er Precache-Einträge anhand von Inhalts-Hashes versioniert – Assets mit gleichem Inhalt werden nicht erneut geladen, geänderte Assets werden sofort aktualisiert. Das ist die produktionsreife Lösung für Cache-Invalidierung ohne manuelle Version-Bumps.
8. Debugging und Update-Mechanismus
Service Worker sind berüchtigt dafür, Debugging schwierig zu machen. Chrome DevTools bietet unter Application → Service Workers eine vollständige Übersicht: aktiver Service Worker, wartender Service Worker, Cache Storage mit allen gecachten Ressourcen und Netzwerklogs gefiltert nach Service Worker. Die Checkbox "Update on reload" erzwingt bei jedem Seitenreload einen neuen Service Worker und umgeht die Warteperiode – unverzichtbar für die Entwicklung. "Bypass for network" deaktiviert den Service Worker temporär und hilft beim Isolieren von Netzwerkproblemen.
Das Update-Verhalten des Service Workers ist eines der häufigsten Quellen von Verwirrung in der Produktion. Wenn eine neue Version des Service Workers registriert wird, bleibt die alte Version aktiv, bis alle Tabs geschlossen werden. Das kann dazu führen, dass Nutzer stundenlang mit einem alten Service Worker arbeiten. Die Lösung ist self.skipWaiting() im Install-Handler kombiniert mit self.clients.claim() im Activate-Handler. Damit übernimmt die neue Version sofort die Kontrolle. Alternativ kann die Anwendung einen "Neue Version verfügbar"-Banner anzeigen und den Nutzer auffordern, die Seite zu reloaden.
9. Caching-Strategien im direkten Vergleich
Die Wahl der richtigen Service Worker-Caching-Strategie ist keine akademische Frage – falsche Entscheidungen führen zu veralteten Inhalten bei Nutzern oder zu unnötigem Netzwerk-Traffic. Die folgende Tabelle fasst zusammen, wann welche Strategie die richtige Wahl ist.
| Strategie | Ablauf | Idealer Anwendungsfall | Risiko |
|---|---|---|---|
| Cache First | Cache → Netz (Fallback) | Statische Assets (CSS, JS, Fonts) | Veraltete Assets ohne Cache-Busting |
| Network First | Netz → Cache (Fallback) | API-Daten, dynamische Seiten | Langsam bei schlechter Verbindung |
| Stale While Revalidate | Cache sofort + Netz-Update im Hintergrund | News-Feeds, Produktlisten | Nutzer sieht kurz veraltete Daten |
| Cache Only | Nur Cache | Precached App Shell | Fehlschlag ohne Precaching |
| Network Only | Nur Netz | Zahlungsabschluss, Login | Kein Offline-Support |
In der Praxis kombiniert man mehrere Strategien innerhalb eines einzigen Service Workers: statische Assets per Cache First, API-Endpunkte per Network First mit Cache-Fallback, die App Shell per Cache Only und sicherheitskritische Endpunkte per Network Only. Workbox macht diese Kombination durch mehrere unabhängige registerRoute()-Aufrufe elegant handhabbar, ohne dass der Fetch-Handler zu einem unübersichtlichen Switch-Statement degeneriert.
Mironsoft
Progressive Web Apps, Service Worker und Offline-First-Architekturen
PWA mit echtem Offline-Support entwickeln?
Wir planen und implementieren Service Worker, Caching-Strategien und Background Sync für Progressive Web Apps – mit messbaren Ladezeit-Verbesserungen und robustem Offline-Betrieb.
PWA-Audit
Lighthouse-Analyse, Service Worker Check und Caching-Strategie-Review für bestehende Anwendungen
Workbox-Integration
Workbox-Setup mit Webpack oder Vite, Precaching-Manifest und automatische Cache-Invalidierung
Background Sync
Offline-Aktionen mit Background Sync und IndexedDB – Formulare und API-Calls auch ohne Verbindung
10. Zusammenfassung
Der Service Worker ist die technische Grundlage für Offline-First Progressive Web Apps. Sein Lebenszyklus aus Install, Activate und Fetch gibt Entwicklern vollständige Kontrolle darüber, wie und wann Assets gecacht und Netzwerkanfragen beantwortet werden. Die Wahl der richtigen Caching-Strategie – Cache First für statische Assets, Network First für dynamische Daten, Stale While Revalidate für Feeds – ist die entscheidende Designentscheidung, die über Geschwindigkeit und Aktualität der Anwendung bestimmt. Background Sync und Push API erweitern den Service Worker um Fähigkeiten, die klassische Webanwendungen nicht besitzen.
Workbox reduziert den Boilerplate-Code erheblich und macht Service Worker für Produktionsprojekte wartbar. Die Integration in moderne Build-Tools wie Vite und Webpack automatisiert die Precache-Manifest-Generierung und stellt sicher, dass geänderte Assets sofort invalidiert werden. Wer Service Worker von Anfang an in die Architektur einplant statt sie nachträglich hinzuzufügen, baut Anwendungen, die schneller, zuverlässiger und nutzerfreundlicher sind – und die der Browser als installierbare App anbieten kann.
Service Worker & Offline-First — Das Wichtigste auf einen Blick
Lebenszyklus
Install (Precaching) → Activate (Cache-Cleanup) → Fetch (Strategie-Entscheidung). skipWaiting() und clients.claim() für sofortige Aktivierung neuer Versionen.
Caching-Strategien
Cache First für statische Assets, Network First für APIs, Stale While Revalidate für Feeds. Workbox kapselt alle Strategien in wiederverwendbare Module.
Offline-First Pattern
App Shell vorab cachen, Offline-Fallback-Seite bereitstellen, Background Sync für fehlgeschlagene Aktionen, IndexedDB als offline-fähiger Datenspeicher.
Debugging
Chrome DevTools → Application → Service Workers. "Update on reload" für Entwicklung. Lighthouse PWA-Audit für Produktionschecks.