Caching‑Strategien, die in der Praxis wirken
6. September 2025
Caching‑Strategien, die in der Praxis wirken
Caching ist einer der effektivsten Hebel für schnelle, stabile Web‑Erlebnisse – und gleichzeitig eine häufige Fehlerquelle. Dieser Leitfaden konzentriert sich auf pragmatische Patterns, die in realen Projekten funktionieren. Ohne Over‑Engineering, mit klaren Defaults.
Ziele und Grundbegriffe
- Freshness vs. Validation: Entweder Antwort ist »frisch« (TTL) oder wird schnell validiert (ETag/Last‑Modified → 304).
- Public vs. Private: Öffentliche Ressourcen (CDN/Browser) vs. personenbezogene Inhalte (nur Browser, oder gezielt am CDN vorbei).
- TTL & SWR:
max-age(Zeit gültig),s-maxage(für CDNs),stale-while-revalidate(im Hintergrund erneuern),stale-if-error(bei Fehlern alt ausliefern). - Vary/Keying: Auf welche Request‑Attribute (z. B.
Accept-Encoding,Accept-Language,Cookie) wird der Cache unterschieden.
Ebenen des Cachings
- Browser‑Cache: Kostenlos, aber pro Nutzer. Ideal für statische Assets.
- CDN/Edge‑Cache: Entlastet Origin, kurze Latenz global. Ideal für Assets & »public« APIs/HTML.
- App/Server‑Cache: In‑Memory (z. B. LRU), Redis, DB‑Query‑Cache. Schnell, aber invalidierungspflichtig.
- Client‑State‑Cache: SWR/React Query. Entkoppelt UI von Netzwerk, kontrolliert Staleness.
HTTP‑Caching: die wichtigsten Direktiven
Cache-Control: public, max-age=31536000, immutable # Assets mit Fingerprint
Cache-Control: public, s-maxage=300, max-age=0, stale-while-revalidate=60 # HTML/API über CDN
Cache-Control: private, no-store # Hochsensible, personalisierte Inhalte
ETag: "abc123" # Validierungstoken für 304
Vary: Accept-Encoding, Accept-Language # Unterschiedliche RepräsentationenTipps:
- Nutze
s-maxagefür CDNs (überschreibtmax-agefür Shared Caches). immutablenur bei versionierten Assets (z. B.app.7f3c1.js).- Bevorzuge ETag‑Validierung für häufig wechselnde, aber cachebare Ressourcen.
Pragmatiker‑Patterns (Startkonfigurationen)
- Statische Assets (JS/CSS/Fonts/Images, fingerprinted)
Cache-Control: public, max-age=31536000, immutable- Fingerprinting über Dateinamen (Hash) ist Pflicht.
- HTML‑Dokumente (öffentlich, aber dynamisch)
Cache-Control: public, s-maxage=300, max-age=0, stale-while-revalidate=60- CDN liefert sofort, revalidiert im Hintergrund; Browser hält praktisch nicht.
- GET‑APIs (öffentlich/teil‑öffentlich)
ETagsetzen; Client sendetIf-None-Match→ schnelle 304.Cache-Control: public, s-maxage=60–300, stale-while-revalidate=60je nach Freshness‑Bedarf.
- Authentifizierte Seiten/API
- Standard:
Cache-Control: private, max-age=0oderno-storebei Sensitivem. - Alternative (fortgeschritten): Personalisierung nur clientseitig; HTML bleibt public‑cachebar (Edge‑Key ignoriert Auth‑Cookies).
- Bilder
- Responsive Sets (
srcset,sizes), moderne Formate (AVIF/WebP), serverseitige Limits (Breite/Höhe) und Lazy Loading. Caching wie bei Assets.
Beispiel: Express/Node Header‑Setup
// Assets (mit Fingerprints)
app.use(
"/assets",
express.static("public/assets", {
setHeaders: (res) => {
res.set("Cache-Control", "public, max-age=31536000, immutable");
},
})
);
// HTML (CDN‑freundlich)
app.get("/", (req, res) => {
res.set(
"Cache-Control",
"public, s-maxage=300, max-age=0, stale-while-revalidate=60"
);
res.send(renderHome());
});
// API mit ETag
app.get("/api/items", (req, res) => {
const body = JSON.stringify(getItems());
const etag =
'W/"' +
require("crypto").createHash("sha1").update(body).digest("hex") +
'"';
if (req.headers["if-none-match"] === etag) return res.status(304).end();
res.set("ETag", etag);
res.set(
"Cache-Control",
"public, s-maxage=120, max-age=0, stale-while-revalidate=60"
);
res.type("application/json").send(body);
});CDN‑Spezifika (Surrogate Keys & Invalidation)
- Nutze Surrogate/Cache‑Tags, um zusammenhängende Inhalte gezielt zu invalidieren (z. B. alle Seiten einer Kategorie).
- Strategien: Soft‑TTL (SWR) + selektive Purges statt globaler »Panic Purge«.
- Achte auf Cache‑Keys: Ignoriere irrelevante Cookies/Headers, um Fragmentierung zu vermeiden.
Client‑seitiges Caching (SWR/React Query)
staleTime: Wie lange Daten als frisch gelten (UI zeigt sofort an, optional Hintergrund‑Refresh).cacheTime: Wie lange im Speicher bleiben (Tab‑Wechsel etc.).- Pattern: Optimistic Updates + Hintergrund‑Revalidation.
Beispiel (SWR):
import useSWR from "swr";
const fetcher = (url) =>
fetch(url, { headers: { accept: "application/json" } }).then((r) => r.json());
const { data, error, isLoading } = useSWR("/api/items", fetcher, {
staleTime: 30000,
revalidateOnFocus: true,
});Häufige Stolpersteine
- »Vary: Cookie« killt den CDN‑Hit‑Rate. Halte Auth‑Cookies aus dem Cache‑Key, wenn möglich.
- Fehlende
ETag/Validatoren → unnötige Bytes statt 304s. - HTML versehentlich ewig gecached (kein Fingerprint, aber
immutable). no-storezu großzügig → verschenkter Speed.- Zuviele Varianten (Locale, Device, AB‑Tests) ohne klares Keying → Cache‑Fragmentierung.
Checkliste (Start‑Defaults)
- Assets: Fingerprinting +
public, max-age=31536000, immutable. - HTML:
public, s-maxage=300, max-age=0, stale-while-revalidate=60. - APIs:
ETag+s-maxage+ 304‑Flow. - Auth:
private, max-age=0oder personalisierte Teile clientseitig. - CDN: Sauberes Cache‑Key, Tags für Purges, keine unnötigen
Vary‑Header.
Nächste Schritte
- Miss Baseline: CDN‑Hit‑Rate, TTFB, Transfer‑Volumen, 304‑Quote.
- Setze die oben genannten Defaults route‑/asset‑spezifisch um.
- Führe ein »Cache‑Budget« ein (z. B. HTML TTL, API TTL, Purge‑SLA).
- Dokumentiere Regeln im Repo (
/docs/caching-guidelines.md) und prüfe sie in PRs.
