JS
() =>
JavaScript · PWA · Offline-First · Service Worker
Service Worker & Offline-First
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.

18 Min. Lesezeit Cache API · Background Sync · Workbox · Push API Chrome · Firefox · Safari · Edge

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.

11. FAQ: Service Worker & Offline-First

1Warum nur unter HTTPS?
Service Worker sitzen als programmierbarer Proxy zwischen App und Netz. Ohne TLS könnten Angreifer den Service Worker selbst manipulieren. Localhost ist für Entwicklung ausgenommen.
2Was passiert bei einem Fehler im Service Worker?
Der Browser deaktiviert ihn und fällt auf normale Netzwerkanfragen zurück. Die App läuft weiter, verliert aber den Offline-Support. DevTools zeigt den Fehler unter Application → Service Workers.
3Wie oft prüft der Browser auf Updates?
Bei jeder Navigation und maximal alle 24 Stunden. skipWaiting() im Install-Handler sorgt für sofortige Aktivierung ohne Warten auf Tab-Schließung.
4Kann ein Service Worker auf LocalStorage zugreifen?
Nein. Kein DOM, kein LocalStorage. Für persistente Daten im Service Worker: Cache API oder IndexedDB — beide sind von Haupt-Thread und Service Worker aus erreichbar.
5Cache API vs. HTTP-Cache?
HTTP-Cache wird vom Browser automatisch via Cache-Control gesteuert. Cache API wird vollständig vom JavaScript-Code kontrolliert — was, wie lange und in welcher Version gecacht wird.
6Wie entferne ich einen Service Worker?
DevTools → Application → Service Workers → Unregister. Programmatisch via navigator.serviceWorker.getRegistrations(). Caches separat über caches.delete() leeren.
7Unterstützt Safari Service Worker vollständig?
Cache API und Basis-Service Worker seit Safari 11.1. Background Sync und Push API haben eingeschränkte Unterstützung. Feature Detection und graceful Degradation einplanen.
8Warum Workbox statt manuellem Code?
Workbox kapselt Strategien, verhindert unbegrenztes Cache-Wachstum und automatisiert Precaching-Manifest-Generierung. Typische Fehler wie fehlende Cache-Invalidierung sind eingebaut behandelt.
9Wie testet man einen Service Worker?
msw (Mock Service Worker) für Unit-Tests des Fetch-Handlers. Playwright und Puppeteer für Integrationstests mit Offline-Simulation über browser.setOffline(true).
10Wie groß darf der Cache sein?
Cache, IndexedDB und LocalStorage teilen ein Kontingent von einigen MB bis mehreren GB. navigator.storage.estimate() gibt das verbleibende Kontingent zurück — proaktiv bereinigen.