Zruck zum Blog, du Zniachtl
Juli 2026·4 Min. zum LesnVo da KI gmocht

HTTP-Caching-Header vastehn

#http#performance#web

De meisten Performance-Probleme, de Leit mit am CDN oda ana Caching-Schicht lösn wolln, wern eigentlich gratis von drei HTTP-Headern glöst, wenn sie richtig gsetzt sand, und de meisten Apps machn des eher versehentlich falsch ois bewusst. Cache-Control, ETag und Last-Modified steuern, ob a Browser a Ressource neu holt, billig neu validiert oda sofort von da Platte liefert - und der Unterschied zwischn de drei Ergebnisse liegt komplett darin, wia du sie konfigurierst.

Cache-Control: Der Header, der am meisten zählt

Cache-Control sogt dem Browser (und jedem Proxy oda CDN dazwischen), wie long a Antwort wiederverwendet wern darf, ohne den Server nochamoi zu kontaktiern.

Cache-Control: max-age=3600

Des heißt: Für die nächsten 3600 Sekunden frog den Server gor ned erst, verwend die gecachte Kopie. In dem Fenster geht gor koa Request raus, wos das schnellstmögliche Ergebnis is, schneller ois jede serverseitige Optimierung, weil's kan Roundtrip gibt.

Gängige Direktiven:

  • max-age=<Sekunden> - wie long die Antwort aus Sicht von am Browser-Cache frisch is.
  • public - darf von geteilten Caches (CDNs, Proxys) gecacht wern, ned nur vom Browser.
  • private - nur der Browser darf cachen; a geteiltes CDN vor deiner App darf des ned.
  • no-cache - trotz dem Nama erlaubt des Caching, erzwingt aber bei jeder Verwendung Revalidierung beim Server (siehe ETag unten). Für "nie cachen" willst no-store.
  • no-store - gor ned cachen, nirgends. Die richtige Wahl für Antworten mit sensiblen nutzerspezifischen Daten.
Cache-Control: private, no-store          # a user's account settings page
Cache-Control: public, max-age=31536000   # a hashed, versioned JS bundle
Cache-Control: no-cache                   # an HTML page you want revalidated every load

Warum statische Assets an sehr langen max-age verwenden soitn

Wenn deine Build-Pipeline Dateinamen mit Content-Hash ausgibt - app.a3f9c1.js statt app.js -, ändert sich der Dateiname selbst, sobois sich der Inhalt ändert. Dann kannst die konkrete Datei sicher für immer cachen, weil a neiche Version einfach a neiche URL hot:

Cache-Control: public, max-age=31536000, immutable

immutable is a Signal, des weiter geht ois max-age: Es sogt dem Browser, diese Ressource ned amoi bei am Hard Refresh neu zu validiern, weil sich der Inhalt unter dera URL wirklich ned ändern kann. Des macht nur bei gehashten, versionierten Dateinamen Sinn - nie bei am schlichten app.js, des vor Ort überschrieben wird, sonst wär jeder Deploy für scho gladene Browser unsichtbar.

Revalidierung: billiger ois a kompletter Neudownload

Für Ressourcen, de sich ändern, aber trotzdem gecacht wern soin, is max-age ned die ganze Geschichte - du willst a, dass der Browser billig prüft, "hot sich des wirklich gändert?", statt blind neu zu laden. Dafür gibt's ETag und Last-Modified.

app.get('/api/report/:id', async (req, res) => {
  const report = await getReport(req.params.id);
  const etag = `"${hashOf(report)}"`;

  res.set('ETag', etag);
  res.set('Cache-Control', 'no-cache');

  if (req.headers['if-none-match'] === etag) {
    return res.status(304).end(); // not modified — no body sent
  }

  res.json(report);
});

Der Browser schickt den ETag, den er kriagt hot, beim nächsten Request im If-None-Match-Header zruck. Wenn er no passt, antwortet der Server mit 304 Not Modified ganz ohne Body - der Browser verwendet seine gecachte Kopie weiter. Deswegn heißt no-cache ned "ned cachen": es heißt "cachen, aber vorher prüfen", und die Prüfung is weit billiger, ois die ganze Antwort nochamoi zu schickn.

Last-Modified funktioniert genauso mit am Zeitstempel statt ana Hash, zruckgschickt als If-Modified-Since:

res.set('Last-Modified', report.updatedAt.toUTCString());

if (req.headers['if-modified-since'] === report.updatedAt.toUTCString()) {
  return res.status(304).end();
}

Bevorzug ETag, wennst an billigen Hash von da Antwort berechnen kannst - er is präzise (jede Inhaltsänderung invalidiert ihn), während Last-Modified nur Sekunden-Granularität hot und davon abhängt, dass deine Daten Änderungszeiten tatsächlich korrekt tracken.

Kombiniern: a realistische API-Antwort

Cache-Control: private, max-age=60, must-revalidate
ETag: "9f8c3a"

Des sogt: Verwend die gecachte Kopie bis zu 60 Sekunden, ohne überhaupt zu fragen, dann nach dem Fenster revalidiern mit If-None-Match, bevor entweder a 304 ausglieferte oda frische Daten gholt wern. Des is a guter Standard für authentifizierte API-Antworten, de sich gelegentlich, aber ned bei jedem Request ändern - der Client hämmert ned auf den Server ein, hängt aber a ned an minutenaltn Daten fest.

Der Fehler, der "warum aktualisiert sich des ned"-Bugs verursacht

Cache-Control: public, max-age=31536000 auf a schlichten, ned gehashten Dateinamen zu setzn is die häufigste Ursache für "i hob an Fix deployed und die Nutzer sehn no die oide Version". Der Browser hot koa Möglichkeit zu wissn, dass sich der Inhalt gändert hot - die URL is identisch, und du host ihm ausdrücklich gsogt, a Joar lang ned nachzufragn. Die Lösung is ned a kürzerer max-age; es is, der Datei an Content-Hash in den Dateinamen zu gebn, damit a echte Änderung a echte neiche URL produziert, und lange max-age-Werte ausschließlich für URLs zu reservieren, de so funktionieren.

Fazit

Cache-Control entscheidet, ob der Browser überhaupt an Request macht. ETag/Last-Modified entscheiden, wenn er fragt, ob der Server mit am fast gratis 304 antworten kann, statt den Body nochamoi zu schicken. Nutz langes, unveränderliches Caching nur auf content-gehashte URLs; nutz kurzes max-age plus Revalidierung für olles, wos sich unter oaner stabilen URL ändern kann; nutz no-store für olles Sensible, des nie in an Cache gschriebn wern soit.

NeicherReflows und Repaints: Warum si deine Seitn ruckelig anfühltElterA praktischer Leitfodn zur API-Paginierung