---
title: Caching‑Strategien, die in der Praxis wirken
author: Lars Decker
date: 2025-09-06
updated: 2025-09-06
description: ETag, Cache-Control, SWR und CDN praxisnah einsetzen: So werden Web-Apps schneller, stabiler und deutlich günstiger im Betrieb.
tags: performance, caching, http, cdn, api
canonical: https://lars-decker.eu/blog/caching-strategien
---

# 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

```http
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äsentationen
```

Tipps:

- Nutze `s-maxage` für CDNs (überschreibt `max-age` für Shared Caches).
- `immutable` nur bei versionierten Assets (z. B. `app.7f3c1.js`).
- Bevorzuge ETag‑Validierung für häufig wechselnde, aber cachebare Ressourcen.

---

## Pragmatiker‑Patterns (Startkonfigurationen)

1. Statische Assets (JS/CSS/Fonts/Images, fingerprinted)

- `Cache-Control: public, max-age=31536000, immutable`
- Fingerprinting über Dateinamen (Hash) ist Pflicht.

1. 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.

1. GET‑APIs (öffentlich/teil‑öffentlich)

- `ETag` setzen; Client sendet `If-None-Match` → schnelle 304.
- `Cache-Control: public, s-maxage=60–300, stale-while-revalidate=60` je nach Freshness‑Bedarf.

1. Authentifizierte Seiten/API

- Standard: `Cache-Control: private, max-age=0` oder `no-store` bei Sensitivem.
- Alternative (fortgeschritten): Personalisierung nur clientseitig; HTML bleibt public‑cachebar (Edge‑Key ignoriert Auth‑Cookies).

1. 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

```js
// 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):

```js
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-store` zu 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=0` oder 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.

