Cache-Hölle: Wenn alte Assets live bleiben – Ursachen, Debugging, saubere Lösung

Cache-Hölle: Wenn alte Assets live bleiben

Du deployest, aber Nutzer sehen weiter das alte CSS/JS? Willkommen in der Cache-Hölle. Hier ist der Reality-Check: wo es cached, wie du’s findest, und wie du’s dauerhaft sauber machst.

„Hab doch deployed!“ – und trotzdem: Styles sind alt, JS ist alt, Bilder sind alt. Du machst Hard Reload, es ist weg. Nutzer schreiben weiter: „Bei mir ist’s kaputt.“

Das ist kein Bug, das ist Cache. Und zwar nicht „der Cache“, sondern oft gleich mehrere: Browser, CDN, Reverse Proxy, Service Worker, manchmal sogar Zwischen-Caches.

Gute Nachricht: Du kannst das komplett in den Griff bekommen. Schlechte Nachricht: Mit „Purge CDN und hoffen“ baust du dir meistens nur eine neue Runde Hölle.

Wo wird überhaupt gecacht? (Spoiler: überall)

Browser Cache

Der Browser cached Assets basierend auf Cache-Control/ETag/Last-Modified. Wenn du dieselbe URL wiederverwendest (z.B. app.css), ist die Wahrscheinlichkeit hoch, dass irgendwer noch die alte Version hat.

CDN / Reverse Proxy

CDNs cachen oft aggressiver als du denkst (und mit eigenen Regeln: s-maxage, Edge-TTL, Cache Keys, „Cache Everything“). Wenn du HTML am Edge cached, referenziert dein HTML vielleicht noch alte Assets.

Service Worker

Service Worker sind der „Cache-Boss“. Wenn du PWA/Offline-Cache nutzt (oder ein Framework dir das eingebaut hat), kann der SW Requests abfangen und aus seinem Cache bedienen – unabhängig von Browser/CDN.

Die häufigsten Ursachen (und warum sie so fies sind)

1) Du überschreibst Assets unter derselben URL

app.css bleibt app.css. Du deployest neue Inhalte, aber Browser/CDN sehen: „kenne ich“. Ergebnis: alte Version bleibt, bis TTL abläuft oder revalidated wird.

  • Fix: Asset Fingerprinting (Hash im Dateinamen): app.4f3a1c.css statt app.css.
  • Fix: Lange Cache-Zeiten nur für gehashte Assets.

2) HTML wird zu lange gecacht (und zeigt auf alte Assets)

Selbst wenn deine Assets perfekt gehasht sind: Wenn Nutzer noch ein altes HTML aus dem Cache bekommen, referenziert das HTML die alten Hash-Dateien.

  • Fix: HTML kurz cachen und revalidieren lassen (statt monatelang).
  • Fix: CDN-Regel: HTML nicht „Cache Everything“ ohne Plan.

3) Service Worker liefert alte Bundles aus

SW kann „stale“ Assets ausliefern, bis ein Update sauber durchläuft. Das sieht dann aus wie: „Nur bei manchen Usern alt – und die bleiben alt.“

  • Fix: SW-Update-Flow sauber: neue Version erkennen, Nutzer informieren/refresh ermöglichen.
  • Fix: Caching-Strategie prüfen (z.B. nicht „cache-first“ für HTML ohne Update-Mechanik).

4) CDN Cache-Key ist falsch (Query-Strings, Cookies, Vary)

Wenn dein CDN Cache-Key Dinge ignoriert oder zu viel unterscheidet, kriegst du entweder „falsche Version“ oder „Cache hitzt nie“. Beides ist schlecht.

  • Fix: Cache-Key bewusst definieren: meist URL + relevante Header, aber nicht „alles“.
  • Fix: Vary sparsam und bewusst einsetzen (sonst Cache-Split).

5) Revalidation missverstanden: ETag/304 hilft nicht, wenn du falsche URLs nutzt

ETag/Last-Modified sind gut, aber sie sind keine Wunderwaffe gegen „gleiche URL, anderer Inhalt“. Du willst trotzdem versionierte Asset-URLs, sonst sind einzelne Clients immer wieder „hinterher“.

Symptom → wahrscheinlichster Cache-Ort → Quick Fix

Symptom Wo cached es vermutlich? Quick Fix
Hard Reload bei dir hilft, Nutzer sehen weiter alt CDN/Proxy oder Service Worker SW prüfen; CDN HTML kurz cachen; Asset-URLs versionieren
Nur CSS ist alt, JS ist neu Browser Cache (CSS) oder CDN Edge TTL CSS fingerprinten; Cache-Control für gehashte Assets lang
Nur bestimmte Länder/Netze sehen alt CDN PoP / ISP Cache / Edge Purge gezielt (HTML), Cache-Key prüfen
SPA zeigt alte UI, bis Tab geschlossen wird Service Worker oder aggressives App-Shell Caching Update-Flow + Refresh-Mechanik, HTML nicht cache-first
Bilder bleiben alt trotz Austausch Browser/CDN Asset Cache Bild-URL versionieren (Hash oder neuer Pfad), nicht überschreiben

So setzt du’s um

So setzt du’s um: Debug-Runbook (15–30 Minuten)

  1. Reproduzieren: Betroffene URL öffnen, DevTools → Network → „Disable cache“ (nur für DevTools) aktivieren.
  2. Headers prüfen: Response Headers für HTML und für CSS/JS anschauen: Cache-Control, ETag, Age, ggf. CDN-Header (z.B. „cache status“).
  3. 304/200 verstehen: Kommt 304? Dann arbeitet Revalidation. Kommt 200 aus „disk cache“/„memory cache“? Dann Browsercache. Kommt 200 mit CDN-Hit? Dann Edge.
  4. Service Worker prüfen: DevTools → Application → Service Workers: aktiv? „Bypass for network“ testen. Wenn’s dann plötzlich „neu“ ist: SW ist dein Schuldiger.
  5. Asset-URLs checken: Referenziert HTML wirklich die neue Datei (Hash)? Wenn HTML alt ist, siehst du hier sofort die alte Referenz.

Saubere Lösung (die nicht jede Woche wiederkommt)

  1. Fingerprints für Assets: CSS/JS/Bilder, die du deployest, bekommen Hash im Dateinamen.
  2. Cache-Regel: Gehashte Assets: lange Cache-Zeit (optional „immutable“), weil URL sich bei Änderung ändert.
  3. HTML-Regel: HTML kurz cachen oder revalidieren (damit neue Asset-Referenzen schnell ankommen).
  4. CDN Purge nur gezielt: wenn nötig, dann eher HTML/Entry-Points purgen, nicht „alles“.
  5. Service Worker Update-Flow: neue SW-Version erkennen, Nutzer kontrolliert refreshen lassen.
Merksatz:
HTML kurz & revalidate.
Assets mit Hash lang & immutable.

Typische Fehler & wie du sie vermeidest

  • Fehler: „Cache-Control: max-age=31536000“ für alles, inkl. HTML. Fix: HTML separat behandeln (kurz/revalidate), Assets mit Hash lang.
  • Fehler: Assets werden überschrieben, URL bleibt gleich. Fix: Hash/Fingerprinting, nicht überschreiben.
  • Fehler: Service Worker wird einmal eingebaut und nie wieder angefasst. Fix: Update-Strategie + klare Ownership.
  • Fehler: „Purge All“ als Standard. Fix: gezielt purgen (Entry-Points) und Versioning korrekt machen.
  • Fehler: Query-String Cache Busting wild gemischt. Fix: Hash im Dateinamen ist stabiler und CDN-freundlicher.

Fazit: Cache ist nicht dein Feind – fehlende Strategie ist es

Cache-Hölle entsteht fast immer durch dieselbe Kombi: gleiche Asset-URLs + lange TTLs + HTML zu aggressiv gecacht (und manchmal ein Service Worker obendrauf).

Nächster Schritt: Implementiere Fingerprinting für Assets und trenne Cache-Regeln strikt: HTML kurz/revalidate, gehashte Assets lang/immutable. Danach brauchst du „Purge All“ eigentlich nie wieder.

FAQ

Warum hilft Hard Reload nur bei mir, aber nicht bei Nutzern?

Weil du damit primär deinen Browsercache umgehst. Wenn das alte Asset im CDN/Proxy oder im Service Worker steckt, bleibt es für andere Nutzer trotzdem alt.

max-age ist die Cache-Zeit für Clients (Browser). s-maxage ist für Shared Caches (z.B. CDNs/Proxies) und kann abweichend sein. Das ist genau der Grund, warum „bei mir neu“ und „für Nutzer alt“ gleichzeitig passieren kann.

Weil sich bei jeder Änderung die URL ändert. Cache darf dann aggressiv sein, ohne dass alte Versionen „aus Versehen“ weiterleben. Du musst nicht raten, ob irgendwo noch eine alte Datei hängt.

Der Cache darf kurz eine ältere Antwort ausliefern („stale“), während im Hintergrund eine neue Version geholt wird („revalidate“). Das kann Performance verbessern, ist aber bei Entry-HTML nur dann sinnvoll, wenn du Versioning sauber im Griff hast.

In DevTools (Application → Service Workers) siehst du, ob einer aktiv ist. Wenn „Bypass for network“ plötzlich das Problem löst oder Nutzer „ewig alt“ bleiben, ist der SW sehr wahrscheinlich der Grund.

Wenn du Fingerprinting sauber machst, musst du gehashte Assets praktisch nie purgen. Wenn überhaupt, purgst du gezielt Entry-Points/HTML, damit neue Referenzen schneller überall ankommen.