Caching und Komprimierung in Nginx
Zwei Fragen bestimmen, wie schnell eine Webseite ist: Wie oft wird derselbe Inhalt neu erzeugt, und wie viele Bytes werden über das Netz übertragen? Caching beantwortet die erste Frage: Eine Kopie des einmal erzeugten Inhalts wird gespeichert und bei späteren Anfragen ausgeliefert, ohne ihn erneut zu erzeugen. Komprimierung beantwortet die zweite: Die Antwort wird vor dem Versand verkleinert, und der Browser entpackt sie wieder.
Nginx erledigt beides mit wenigen Direktiven. Diese Anleitung erklärt zunächst, wozu jede einzelne dient, dann, wie sie eingestellt wird, und vor allem, was nicht in den Cache gehört. Die Beispiele sind allgemein gehalten; wählen Sie die Werte passend zu Ihrer Website und testen Sie die Konfiguration nach jeder Änderung.
Kurz gefasst
- Der Browser-Cache erspart wiederkehrenden Besuchern das erneute Herunterladen derselben Dateien.
- gzip verkleinert textbasierte Antworten; auf Bilder und Archive wird es nicht angewendet.
- proxy_cache speichert die Antwort des Backend-Servers und liefert sie bei späteren Anfragen direkt aus.
- Seiten mit Anmeldung und personalisierte Seiten gehören nicht in einen gemeinsamen Cache.
Achtung
Ein falsch eingerichteter Server-Cache kann die Seite eines Benutzers einem anderen Benutzer anzeigen. Aktivieren Sie proxy_cache oder fastcgi_cache erst, wenn sicher ist, dass Seiten mit Anmeldung, Warenkorb oder persönlichen Daten vom Cache ausgenommen sind.
Auf dieser Seite
Drei Werkzeuge: Browser-Cache, Komprimierung, Server-Cache
-
Welcher Cache liegt wo?
„Der Cache“ ist nicht eine einzige Sache; an verschiedenen Stellen des Anfragewegs können drei getrennte Kopien liegen:
- Browser-Cache: Auf dem Gerät des Besuchers. Der Server teilt über Antwort-Header mit: „Diese Datei darfst du so lange aufbewahren“; in dieser Zeit fordert der Browser die Datei nicht erneut an.
- Server-Cache (proxy_cache / fastcgi_cache): Auf der Festplatte des Nginx-Servers. Als Reverse Proxy speichert Nginx die Antwort, die es vom Backend-Server erhalten hat, und beantwortet spätere Anfragen an dieselbe Adresse, ohne das Backend überhaupt anzusprechen. Er gilt für alle Besucher gemeinsam.
- Zwischengeschaltete Caches: Dienste wie ein CDN werten dieselben Header aus und halten eigene Kopien vor. Einzelheiten finden Sie in der Anleitung zum CDN.
Die Komprimierung dagegen hält keine Kopie vor; sie verkleinert die Antwort für den Transport über das Netz. Die drei ersetzen einander nicht, sondern werden zusammen eingesetzt.
: Vergrößern -
Browser-Cache für statische Dateien
CSS-, JavaScript-, Bild- und Schriftdateien ändern sich selten. Die Direktive
expiresfügt der Antwort die HeaderExpiresundCache-Control: max-agehinzu; mitadd_header Cache-Controlwerden zusätzliche Werte wiepublicoderimmutablegesetzt.Entscheidend für die Dauer ist, ob sich die Adresse der Datei ändert:
- Versionierte Dateinamen (z. B.
app.3f9a1c.css): Da sich mit dem Inhalt auch der Name ändert, sind eine sehr lange Dauer (etwa ein Jahr) undimmutableunbedenklich.immutablesagt dem Browser: „Bis zum Ablauf brauchst du diese Datei nicht erneut zu prüfen.“ - Dateien mit festem Namen (z. B.
style.css): Bei langer Dauer sehen Besucher Aktualisierungen erst spät. Wählen Sie eine kürzere Dauer oder ergänzen Sie den Dateinamen um eine Version. - HTML-Seiten: erhalten meist
no-cache; der Browser behält die Kopie, fragt aber vor jeder Verwendung beim Server nach.
Nginx# Versionierte Dateien (z. B. /assets/app.3f9a1c.css): Mit dem Inhalt ändert sich auch der Name location ^~ /assets/ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # Übrige statische Dateien: fester Name, daher kürzere Dauer location ~* \.(?:css|js|jpg|jpeg|png|gif|webp|svg|ico|woff2)$ { expires 30d; add_header Cache-Control "public"; } # HTML-Seiten: Die Kopie wird behalten, vor jeder Verwendung wird aber beim Server nachgefragt location / { add_header Cache-Control "no-cache"; try_files $uri $uri/ =404; }Achten Sie auf zwei Einzelheiten. Im ersten Block steht
^~; ohne dieses Zeichen würde die Anfrage/assets/app.cssauf die darunter stehendelocationmit regulärem Ausdruck passen und die kürzere Dauer erhalten. Zweitens werdenadd_header-Direktiven nur dann von der übergeordneten Ebene geerbt, wenn auf der aktuellen Ebene gar keinadd_headersteht; sobald Sie in einerlocationein einzigesadd_headerschreiben, entfallen dort die aufserver-Ebene definierten Sicherheitsheader und müssen wiederholt werden. - Versionierte Dateinamen (z. B.
-
Komprimierung mit gzip
Der Browser teilt in seiner Anfrage mit dem Header
Accept-Encoding: gzipmit, dass er komprimierte Antworten annimmt; Nginx komprimiert die Antwort und sendet sie mitContent-Encoding: gzip. Textbasierte Inhalte wie HTML, CSS, JavaScript, JSON und SVG werden dadurch deutlich kleiner.Nginx# Im http-Block: gilt für alle Websites gzip on; gzip_comp_level 5; gzip_min_length 1024; gzip_vary on; gzip_proxied any; # text/html wird immer komprimiert und gehört nicht in diese Liste gzip_types text/plain text/css text/xml application/json application/javascript application/xml application/rss+xml image/svg+xml;gzip on;schaltet die Komprimierung ein. Für sich allein komprimiert es nur Antworten vom Typtext/html.gzip_typesnennt weitere Inhaltstypen.text/htmlwird immer komprimiert und gehört nicht in die Liste (andernfalls gibt Nginx eine Warnung wegen eines doppelten Typs aus).gzip_min_lengthlässt Antworten unkomprimiert, die kürzer als diese Anzahl Bytes sind (Standardwert 20). Da sehr kleine Antworten nichts gewinnen, wählt man meist eine höhere Schwelle.gzip_comp_levelreicht von 1 bis 9 (Standardwert 1). Höhere Stufen kosten mehr Rechenzeit, während der Gewinn immer kleiner wird; ein mittlerer Wert genügt für die meisten Websites.gzip_vary on;ergänzt die Antwort umVary: Accept-Encoding; so verwechseln zwischengeschaltete Caches komprimierte und unkomprimierte Kopien nicht.gzip_proxied: Standardmäßig komprimiert Nginx keine Antworten auf Anfragen, die über einen anderen Proxy eintreffen (Anfragen mit dem HeaderVia). Steht vor Ihrer Website ein CDN oder ein anderer Proxy, benötigen Sie diese Direktive.
Formate wie JPEG, PNG, WebP, Video, ZIP und PDF sind bereits komprimiert; sie in die Liste aufzunehmen, verschwendet nur Rechenzeit.
Brotli ist ein alternatives Komprimierungsverfahren zu gzip. Es ist im Standardpaket von Nginx nicht enthalten und erfordert die Installation eines separaten Moduls. Ohne das Modul führt
brotli on;zu einem Konfigurationsfehler. Lassen Sie gzip auch dann eingeschaltet, wenn Sie Brotli verwenden; Clients ohne Brotli-Unterstützung erhalten gzip.
: Vergrößern -
Server-Cache mit proxy_cache
Arbeitet Nginx als Reverse Proxy vor einem Backend-Server (siehe Reverse Proxy einrichten), kann es Antworten auf der Festplatte speichern. Die erste Anfrage an eine Adresse geht an das Backend, und die Antwort wird gespeichert (Fehltreffer, MISS); spätere Anfragen werden bis zum Ablauf des Eintrags direkt aus dem Cache beantwortet (Treffer, HIT).
Nginx# Cache-Bereich: Verzeichnis, Schlüsselzone (10 MB), Plattenlimit, Löschfrist für Ungenutztes proxy_cache_path /var/cache/nginx/site levels=1:2 keys_zone=site_cache:10m max_size=1g inactive=60m use_temp_path=off; # 1, wenn ein Sitzungs-Cookie vorhanden ist, sonst 0 (Cookie-Namen an Ihre Anwendung anpassen) map $cookie_PHPSESSID $skip_cache { default 1; "" 0; } server { listen 80; server_name beispiel.de; # Seiten, die für alle gleich aussehen: mit Cache location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache site_cache; proxy_cache_key $scheme$host$request_uri; proxy_cache_valid 200 301 10m; proxy_cache_valid 404 1m; # Anfrage mit Sitzung: nicht aus dem Cache beantworten und die Antwort nicht speichern proxy_cache_bypass $skip_cache; proxy_no_cache $skip_cache; # Abgelaufene Kopie ausliefern, wenn das Backend nicht antworten kann proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_lock on; add_header X-Cache-Status $upstream_cache_status; } # Verwaltungsbereich, Warenkorb und Kontoseiten: kein Cache location ~ ^/(verwaltung|warenkorb|konto)(/|$) { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }proxy_cache_pathdarf nur aufhttp-Ebene stehen: das Verzeichnis für die Dateien, die gemeinsame Speicherzone für die Schlüssel (keys_zone=Name:Größe), die Obergrenze auf der Festplatte (max_size) und das Löschen von Einträgen, die so lange niemand angefordert hat (inactive).proxy_cacheaktiviert die Zone für dieselocation.proxy_cache_keylegt fest, wann zwei Anfragen als „gleich“ gelten. Hängt der Inhalt zusätzlich von etwas anderem ab (etwa einem Sprach-Cookie), muss dieser Wert in den Schlüssel; sonst erhält jemand, der eine andere Sprache angefordert hat, die in der ersten Sprache erzeugte Seite.proxy_cache_validist die Speicherdauer je Statuscode. Sendet das Backend einenCache-Control- oderExpires-Header, hat dieser Vorrang.proxy_cache_bypassverhindert, dass die Anfrage aus dem Cache beantwortet wird,proxy_no_cacheverhindert, dass die Antwort in den Cache geschrieben wird. Die Bedingung greift, wenn der angegebene Wert weder leer noch „0“ ist. Im Beispiel setztmapdie Variable bei Anfragen mit Sitzungs-Cookie auf 1; passen Sie den Cookie-Namen an Ihre Anwendung an.proxy_cache_use_staleerlaubt es, eine abgelaufene Kopie auszuliefern, wenn das Backend einen Fehler meldet oder nicht antwortet; bei kurzen Ausfällen bleibt die Website erreichbar.add_header X-Cache-Status $upstream_cache_status;schreibt den Cache-Status in jede Antwort.
Standardmäßig speichert Nginx nur GET- und HEAD-Anfragen und keine Antworten, die
Set-Cookieenthalten oder derenCache-Control-Headerprivate,no-cacheoderno-storeenthält. Dieses Verhalten mitproxy_ignore_headersabzuschalten, ist die häufigste Ursache dafür, dass persönliche Inhalte nach außen gelangen; verwenden Sie die Direktive nicht, wenn Sie sich nicht sicher sind.
: Vergrößern -
Was gehört nicht in den Cache?
Der Server-Cache gilt für alle Besucher gemeinsam. Die Regel ist einfach: Hängt die Antwort davon ab, wer anfragt, gehört sie nicht in einen gemeinsamen Cache.
- Seiten für angemeldete Benutzer: Mein Konto, Bestellungen, Profil, persönliche Preise oder Inhalte.
- Warenkorb und Bezahlschritte.
- Der Verwaltungsbereich und Anmeldeseiten.
- Antworten mit
Set-Cookie: Werden sie gespeichert, erhält ein anderer Benutzer das Sitzungs-Cookie des ersten. - Formularübermittlungen (POST) und Seiten mit Einmal-Token (z. B. CSRF-Token).
- API-Antworten, die vom Autorisierungsheader abhängen.
Am sichersten ist es, den Cache nur für ausdrücklich ausgewählte Adressen zu aktivieren (Inhalte, die für alle gleich aussehen, etwa Startseite, Produkt- und Artikelseiten) und Verwaltungs- und Kontoadressen in einer eigenen
locationohne Cache zu belassen. Allgemeine Maßnahmen rund um Sitzungen finden Sie in der Anleitung zur Anmelde- und Sitzungssicherheit.
: Vergrößern
Prüfen, ob es funktioniert: X-Cache-Status
Sehen Sie sich nach dem Testen und Neuladen der Konfiguration die Antwort-Header an. Rufen Sie dieselbe Adresse zweimal ab: In der ersten Antwort sollte MISS stehen, in der zweiten HIT.
# Konfiguration testen und, wenn alles in Ordnung ist, neu laden
sudo nginx -t && sudo systemctl reload nginx
# Header statischer Dateien: Cache-Control und Expires sollten erscheinen
curl -s -o /dev/null -D - https://beispiel.de/assets/app.3f9a1c.css | grep -i -E "cache-control|expires"
# Komprimierung: Content-Encoding: gzip und Vary: Accept-Encoding sollten erscheinen
curl -s -o /dev/null -D - -H "Accept-Encoding: gzip" https://beispiel.de/ | grep -i -E "content-encoding|vary"
# Server-Cache: dieselbe Adresse zweimal abrufen (erst MISS, dann HIT)
curl -s -o /dev/null -D - https://beispiel.de/ | grep -i x-cache-status
curl -s -o /dev/null -D - https://beispiel.de/ | grep -i x-cache-status| Wert | Bedeutung |
|---|---|
| MISS | Im Cache lag keine Kopie; die Antwort kam vom Backend (und wurde gespeichert, sofern zulässig). |
| HIT | Die Antwort kam direkt aus dem Cache; das Backend wurde nicht angesprochen. |
| BYPASS | Die Bedingung von proxy_cache_bypass war erfüllt; der Cache wurde übergangen. |
| EXPIRED | Die Kopie war abgelaufen; die Antwort wurde erneut vom Backend geholt. |
| STALE | Das Backend konnte nicht antworten; eine abgelaufene Kopie wurde ausgeliefert. |
| UPDATING | Während der Eintrag erneuert wurde, kam die alte Kopie zum Einsatz. |
| REVALIDATED | Das Backend hat bestätigt, dass die abgelaufene Kopie noch gültig ist. |
Sehen Sie dauerhaft MISS, prüfen Sie die Antwort des Backends: Möglicherweise sendet es Set-Cookie oder Cache-Control: no-store. Der Start einer Sitzung in PHP etwa fügt in der Standardeinstellung Header hinzu, die das Caching verhindern. Wenn Sie den Header auf der Live-Website nicht allen zeigen möchten, können Sie ihn nach Abschluss der Tests wieder entfernen.
Wenn Sie PHP-FPM verwenden: fastcgi_cache
Leitet Nginx PHP nicht an einen anderen Webserver, sondern direkt an PHP-FPM weiter (fastcgi_pass), wird dieselbe Logik mit der fastcgi_cache-Familie umgesetzt: fastcgi_cache_path, fastcgi_cache, fastcgi_cache_valid, fastcgi_cache_bypass, fastcgi_no_cache. Ein wichtiger Unterschied: Für fastcgi_cache_key gibt es keinen Standardwert; er muss ausdrücklich angegeben werden.
fastcgi_cache_path /var/cache/nginx/php levels=1:2 keys_zone=php_cache:10m
max_size=512m inactive=60m;
map $cookie_PHPSESSID $skip_cache {
default 1;
"" 0;
}
server {
listen 80;
server_name beispiel.de;
root /var/www/beispiel.de/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php-fpm.sock; # der Socket-Pfad hängt von der Distribution ab
fastcgi_cache php_cache;
fastcgi_cache_key $scheme$request_method$host$request_uri; # hat keinen Standardwert
fastcgi_cache_valid 200 5m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache-Status $upstream_cache_status;
}
}Zur PHP-FPM-Anbindung selbst siehe die Anleitung Nginx und PHP-FPM.
Den Cache leeren
Hat sich der Inhalt geändert, der Cache liefert aber weiterhin die alte Kopie, gibt es mehrere Wege:
- Die Dauer kurz halten: Für häufig geänderte Seiten genügt meist ein
proxy_cache_validvon wenigen Minuten; ein Leeren ist dann nicht nötig. - Die Dateien löschen: Das Löschen der Dateien im Cache-Verzeichnis leert den gesamten Cache; Nginx holt die Antworten bei den nächsten Anfragen erneut vom Backend. Zielen Sie ausschließlich auf das Verzeichnis, das Sie mit
proxy_cache_pathfestgelegt haben. - Den Schlüssel ändern: Wenn Sie
proxy_cache_keyum einen Versionswert ergänzen und diesen ändern, werden alle alten Einträge auf einmal ungültig; die alten Dateien werden nach Ablauf derinactive-Frist von selbst entfernt. - Eine einzelne Adresse erneuern: Knüpfen Sie die Bedingung von
proxy_cache_bypassan ein Signal, das nur Sie senden können (etwa einen Anfrage-Header, der nur von Ihrer eigenen IP-Adresse akzeptiert wird), erhält diese Anfrage eine frische Antwort vom Backend, und der Eintrag wird erneuert.
# Den gesamten Server-Cache leeren.
# Der Pfad muss das mit proxy_cache_path (bzw. fastcgi_cache_path) festgelegte Verzeichnis sein; kein anderes Verzeichnis angeben.
sudo find /var/cache/nginx/site -type f -deleteEine fertige „Purge“-Funktion, die einzelne Adressen entfernt, ist nach unserem Kenntnisstand in der Open-Source-Version von Nginx nicht eingebaut; sie wird von der kommerziellen Version oder von Modulen von Drittanbietern bereitgestellt. Prüfen Sie in der Dokumentation Ihres eigenen Pakets, was Ihre Installation enthält.
Den Browser-Cache dagegen können Sie vom Server aus nicht leeren; er liegt auf dem Gerät des Besuchers. Deshalb sind versionierte Dateinamen der einzig verlässliche Weg für statische Dateien mit langer Dauer.
Häufige Fehler
- CSS- und JavaScript-Dateien, deren Name sich nie ändert, ein Jahr Dauer geben; Aktualisierungen erreichen die Besucher nicht.
text/htmloder Bildtypen in die Listegzip_typesaufnehmen.proxy_cachefür die gesamte Website aktivieren, ohne das Sitzungs-Cookie zu prüfen.- Die Zeile
proxy_ignore_headers Set-Cookie Cache-Control;kopieren, ohne zu wissen, was sie bewirkt. - Bei Inhalten, die von Sprache, Gerät oder Währung abhängen, diesen Wert nicht in den Cache-Schlüssel aufnehmen.
- In einer
locationeinadd_headerschreiben und nicht bemerken, dass die Sicherheitsheader der übergeordneten Ebene verschwunden sind (siehe Sicherheitsheader). - Das Cache-Verzeichnis an einem Ort anlegen, an dem der Benutzer, unter dem Nginx läuft, nicht schreiben darf.
Checkliste
- Statische Dateien erhalten über
expireseine Dauer; eine lange Dauer nur bei versionierten Dateinamen. - gzip ist eingeschaltet;
gzip_typesenthält nur textbasierte Typen, dazugzip_vary on. - Der Server-Cache ist nur für Adressen aktiv, die für alle gleich aussehen.
- Anfragen mit Sitzungs-Cookie sind über
proxy_cache_bypassundproxy_no_cacheausgenommen. - Verwaltungsbereich, Warenkorb und Kontoseiten laufen ohne Cache.
- Mit
X-Cache-Statuswurden MISS und HIT beobachtet; angemeldet erscheint BYPASS. - Wie der Cache geleert wird, ist schriftlich festgehalten und erprobt.
- Nach jeder Änderung wurde
nginx -tausgeführt.
Häufige Fragen
Warum sind Änderungen an der Website bei aktiviertem Cache nicht sofort sichtbar?
Weil der Browser oder der Server eine alte, noch nicht abgelaufene Kopie ausliefert. Den Server-Cache können Sie leeren oder seine Dauer verkürzen; für den Browser-Cache sind versionierte Dateinamen bei statischen Dateien die dauerhafte Lösung.
Soll ich gzip oder Brotli verwenden?
gzip wird mit Nginx geliefert und funktioniert in jedem Browser; schalten Sie es zuerst ein. Brotli erfordert ein separates Modul. Können Sie das Modul installieren, lassen sich beide zusammen nutzen; der Browser wählt das Verfahren, das er unterstützt.
Ich sehe ständig MISS. Woran liegt das?
Die häufigsten Ursachen: Das Backend sendet Set-Cookie oder Cache-Control: no-store bzw. private, die Anfrage enthält ein Sitzungs-Cookie, proxy_cache_valid ist nicht definiert, oder die Adresse enthält einen Abfrageparameter, der sich bei jeder Anfrage ändert.
Worin unterscheiden sich proxy_cache und fastcgi_cache?
Die Logik ist dieselbe; der Unterschied liegt darin, woher Nginx die Antwort bezieht. Ist das Backend ein Server, der HTTP spricht (proxy_pass), verwenden Sie proxy_cache; ist es direkt PHP-FPM (fastcgi_pass), verwenden Sie fastcgi_cache.
Brauche ich den Nginx-Cache noch, wenn ich ein CDN nutze?
Beide ergänzen sich. Ein CDN liefert Inhalte von Standorten in der Nähe des Besuchers aus, während der Nginx-Cache die Anwendung bei den Anfragen entlastet, die das CDN an Ihren Server weiterreicht. Sind Ihre Cache-Header korrekt, folgen beide denselben Regeln.
BYK Yazılım Support-Team
Diese Anleitung wird vom Support-Team von BYK Yazılım erstellt und regelmäßig überprüft. Letzte Aktualisierung: 04.10.2026.
Verwandte Anleitungen
- Nginx als Reverse Proxy einrichtenSchritt für Schritt: server-Block, proxy_pass, weitergereichte Header, WebSocket, Timeouts und echte Client-IP.
- Nginx und PHP-FPM: So laufen PHP-Websites mit Nginxfastcgi_pass, SCRIPT_FILENAME, try_files, das Pool-Konzept, Socket-Berechtigungen und der Fehler 502.
- Was ist ein CDN (Content Delivery Network)?Was ein CDN ist, wie es in Betrieb geht und worauf bei echter IP, Cache und Sicherheit des Ursprungsservers zu achten ist.
- 502, 504 und andere Nginx-Fehler: Ursachen und LösungenBedeutung, mögliche Ursachen, Diagnose über die Logs und Lösung für 502, 504, 413, 499, 403 und 404.
Sprechen wir über die Infrastruktur Ihrer Website
BYK Yazılım entwickelt Unternehmenswebsites. Schreiben Sie uns bei Fragen zu Ihrer Website.
Kontakt aufnehmen Unternehmenswebsite