Browser-Caching: Cache-Control-Header richtig setzen
Stand: 3. Oktober 2026 · CAZ Labs
Kurz gesagt
Der Header `Cache-Control` sagt dem Browser, wie lange er eine Datei behalten darf, ohne sie neu zu laden. Für CSS, JavaScript, Bilder und Schriften ist ein Jahr (`max-age=31536000`) ein guter Wert, solange sich der Dateiname bei jeder Änderung ändert. checkfrei wertet ab einer Woche oder mit `immutable` als gut.
Warum das wichtig ist
Wer deine Seite zum zweiten Mal öffnet oder auf eine Unterseite klickt, braucht dasselbe Stylesheet und dieselben Skripte wie beim ersten Aufruf. Mit einem langen Cache-Header holt der Browser sie aus dem eigenen Speicher, ohne beim Server überhaupt anzufragen. Das spart Datenvolumen und macht jeden weiteren Seitenaufruf spürbar schneller.
Fehlt der Header, rät der Browser. Er nimmt dann oft das Änderungsdatum der Datei als Anhaltspunkt und speichert sie für eine kurze, schwer vorhersagbare Zeit. Oder er fragt bei jedem Aufruf nach, ob sich etwas geändert hat. Diese Rückfrage ist klein, kostet auf dem Handy aber pro Datei eine Runde zum Server.
Zur Ehrlichkeit gehört: Beim allerersten Besuch bringt Caching nichts. Genau diesen Erstbesuch simuliert der Performance-Wert von Google, die Zahl steigt durch Caching also kaum. PageSpeed Insights weist in den Diagnosen trotzdem auf zu kurze Cache-Zeiten hin. Der Gewinn liegt bei wiederkehrenden Besuchern und bei allen, die sich durch mehrere Seiten klicken, in einem Shop also bei fast jedem.
So prüfst du es selbst
Auf der Kommandozeile geht es mit curl -I https://deine-domain.de/pfad/style.css.
Mit dem kostenlosen Check von checkfrei geht es ohne Entwicklertools. Wir lesen den Cache-Control-Header einer eigenen CSS- oder JS-Datei aus. Ab einer Woche (max-age=604800) oder mit immutable ist es gut, kürzer gibt eine Warnung, ganz ohne Angabe einen Fehler.
- Öffne deine Seite, drück F12 und geh in den Reiter Netzwerk.
- Lade die Seite neu und klick eine CSS- oder JS-Datei von deiner eigenen Domain an.
- Such unter Response Headers (Antwort-Header) nach
cache-control. Steht dortmax-age=31536000, sind das 365 Tage in Sekunden.
So behebst du es
Vorher eine Sache, die viele übersehen: Ein langer Cache ist nur dann unproblematisch, wenn sich die Adresse einer Datei ändert, sobald du sie änderst. Sonst sehen Stammbesucher nach einem Design-Update wochenlang das alte CSS. WordPress hängt deshalb an eingebundene Dateien ?ver= mit einer Versionsnummer an, moderne Build-Tools schreiben einen Hash in den Dateinamen (app.3f9c2a.js). Pflegst du Dateien von Hand, änderst du bei jeder Änderung den Namen oder einen Parameter wie style.css?v=2.
Die HTML-Seite selbst cachst du im Browser nicht lange. Sie soll bei jedem Besuch frisch sein, sonst tauchen neue Inhalte erst verspätet auf.
Bei Shopify, Wix und Jimdo kommen die Dateien über das CDN der Plattform, eigene Header kannst du dort nicht setzen. Die Cache-Zeiten sind dort in der Regel vernünftig. Meldet checkfrei trotzdem etwas, bleibt nur der Support.
WordPress
Caching-Plugins wie WP Rocket oder W3 Total Cache kümmern sich auch um den Browser-Cache oder haben eine Einstellung dafür. Auf Apache-Servern schreiben sie die passenden Regeln in die .htaccess. Läuft dein Hoster mit nginx, wird die .htaccess ignoriert. Dann muss der Hoster die Regel setzen. Frag beim Support konkret nach "Cache-Control mit langem max-age für CSS, JS, Bilder und Schriften".
Apache (.htaccess)
Das Modul mod_expires setzt für jeden Dateityp eine Ablaufzeit und schreibt dabei sowohl Expires als auch Cache-Control: max-age. Mit mod_headers legst du Cache-Control zusätzlich exakt fest, diese Zeile gewinnt dann. Beides zusammen schadet nicht.
# Browser-Caching für statische Dateien
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType text/javascript "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/avif "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType text/html "access plus 0 seconds"
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(css|js|webp|avif|jpe?g|png|svg|woff2)$">
Header set Cache-Control "public, max-age=31536000"
</FilesMatch>
</IfModule>nginx
expires 1y setzt Cache-Control: max-age=31536000 und einen Expires-Header. Willst du zusätzlich immutable, nimm statt expires eine eigene add_header-Zeile. Beides zusammen erzeugt zwei Cache-Control-Header.
Eine Falle, in die fast jeder einmal tappt: Hat ein location-Block eigene add_header-Zeilen, erbt er keine add_header-Angaben aus dem server-Block mehr. Sicherheitsheader wie HSTS fehlen dann ausgerechnet bei diesen Dateien. Wiederhol sie im Block oder binde sie per include aus einer gemeinsamen Datei ein.
location ~* \.(css|js|webp|avif|jpe?g|png|svg|woff2)$ {
expires 1y;
}
# Variante mit immutable, nur bei versionierten Dateinamen:
# location ~* \.(css|js)$ {
# add_header Cache-Control "public, max-age=31536000, immutable";
# }Next.js
Dateien unter /_next/static liefert Next.js bereits mit public, max-age=31536000, immutable aus, sie haben gehashte Namen. Für Dateien aus dem Ordner public gilt das nicht. Die bekommen ihre Header über headers() in der next.config. Da sich ihr Name bei Änderungen nicht ändert, nimm dort lieber eine kürzere Zeit, etwa 30 Tage.
// next.config.js
module.exports = {
async headers() {
return [
{
source: "/bilder/:pfad*",
headers: [{ key: "Cache-Control", value: "public, max-age=2592000" }],
},
];
},
};Typische Fehler
- Lange Cache-Zeiten ohne Versionierung. Danach wundert sich jemand, warum der Kunde das neue Logo nicht sieht.
- Die HTML-Seite für Stunden im Browser cachen. Neue Blogartikel und geänderte Preise erscheinen dann verspätet.
no-cachefür "nichts speichern" halten. Es bedeutet "vor jeder Nutzung beim Server nachfragen". Wirklich nichts speichern heißtno-store.
So prüft checkfrei
Wir prüfen den Cache-Control-Header einer eigenen CSS- oder JS-Datei. Ab einer Woche oder "immutable" ist es gut, kürzer gibt eine Warnung, ohne Angabe einen Fehler.
Gewicht 1 von 3 im Bereich Ladezeit.
Häufige Fragen
Was ist der Unterschied zwischen Expires und Cache-Control?
Expires nennt ein festes Ablaufdatum und stammt aus der Anfangszeit von HTTP. Cache-Control: max-age nennt eine Dauer in Sekunden und hat Vorrang, wenn beide gesetzt sind. Beide zu haben schadet nicht, entscheidend ist Cache-Control.
Wie bekommen Besucher meine Änderungen, wenn CSS ein Jahr gecacht wird?
Über eine neue Adresse. Sobald sich der Dateiname oder ein Parameter wie ?ver= ändert, lädt der Browser die Datei neu. Deinen eigenen Browser-Cache leerst du mit Strg+F5 (am Mac Cmd+Shift+R). Den Seiten-Cache eines WordPress-Plugins leerst du im Plugin, das ist ein anderer Speicher.
Ist ein Jahr nicht zu lang?
Bei versionierten Dateien nicht, denn jede Änderung erzeugt eine neue Adresse. Bei Dateien, deren Name gleich bleibt, sind eine Woche bis 30 Tage der vernünftigere Kompromiss.
Jetzt deine Seite prüfen
checkfrei prüft Browser-Caching und 68 weitere Punkte in einem Durchgang. Kostenlos und ohne Anmeldung.