Zum Inhalt springen
Planen wir gemeinsam die passende Software für Ihre Prozesse. Demo oder Angebot: +90 546 737 48 29

TR EN DE

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

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

    Schema: Der Browser-Cache liegt auf dem Gerät des Besuchers, die Komprimierung wirkt auf dem Netzweg, der Server-Cache liegt zwischen Nginx und dem Backend-Server : Vergrößern
  2. Browser-Cache für statische Dateien

    CSS-, JavaScript-, Bild- und Schriftdateien ändern sich selten. Die Direktive expires fügt der Antwort die Header Expires und Cache-Control: max-age hinzu; mit add_header Cache-Control werden zusätzliche Werte wie public oder immutable gesetzt.

    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) und immutable unbedenklich. immutable sagt 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.css auf die darunter stehende location mit regulärem Ausdruck passen und die kürzere Dauer erhalten. Zweitens werden add_header-Direktiven nur dann von der übergeordneten Ebene geerbt, wenn auf der aktuellen Ebene gar kein add_header steht; sobald Sie in einer location ein einziges add_header schreiben, entfallen dort die auf server-Ebene definierten Sicherheitsheader und müssen wiederholt werden.

  3. Komprimierung mit gzip

    Der Browser teilt in seiner Anfrage mit dem Header Accept-Encoding: gzip mit, dass er komprimierte Antworten annimmt; Nginx komprimiert die Antwort und sendet sie mit Content-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 Typ text/html.
    • gzip_types nennt weitere Inhaltstypen. text/html wird immer komprimiert und gehört nicht in die Liste (andernfalls gibt Nginx eine Warnung wegen eines doppelten Typs aus).
    • gzip_min_length lä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_level reicht 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 um Vary: 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 Header Via). 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.

    Schema: Der Browser sendet Accept-Encoding: gzip, Nginx komprimiert die textbasierte Antwort und liefert sie mit Content-Encoding: gzip zurück; Bilder und Archive werden nicht komprimiert : Vergrößern
  4. 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_path darf nur auf http-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_cache aktiviert die Zone für diese location.
    • proxy_cache_key legt 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_valid ist die Speicherdauer je Statuscode. Sendet das Backend einen Cache-Control- oder Expires-Header, hat dieser Vorrang.
    • proxy_cache_bypass verhindert, dass die Anfrage aus dem Cache beantwortet wird, proxy_no_cache verhindert, dass die Antwort in den Cache geschrieben wird. Die Bedingung greift, wenn der angegebene Wert weder leer noch „0“ ist. Im Beispiel setzt map die Variable bei Anfragen mit Sitzungs-Cookie auf 1; passen Sie den Cookie-Namen an Ihre Anwendung an.
    • proxy_cache_use_stale erlaubt 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-Cookie enthalten oder deren Cache-Control-Header private, no-cache oder no-store enthält. Dieses Verhalten mit proxy_ignore_headers abzuschalten, 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.

    Ablaufdiagramm: Eine Anfrage trifft ein; liegt eine gültige Kopie im Cache, wird sie direkt ausgeliefert (HIT); andernfalls geht die Anfrage an den Backend-Server, die Antwort wird gespeichert und zurückgegeben (MISS) : Vergrößern
  5. 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 location ohne Cache zu belassen. Allgemeine Maßnahmen rund um Sitzungen finden Sie in der Anleitung zur Anmelde- und Sitzungssicherheit.

    Vergleich: Seiten, die für alle gleich aussehen, und statische Dateien werden gecacht; Seiten mit Anmeldung, Warenkorb, Verwaltungsbereich und Antworten mit Set-Cookie nicht : 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.

Bash
# 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
WertBedeutung
MISSIm Cache lag keine Kopie; die Antwort kam vom Backend (und wurde gespeichert, sofern zulässig).
HITDie Antwort kam direkt aus dem Cache; das Backend wurde nicht angesprochen.
BYPASSDie Bedingung von proxy_cache_bypass war erfüllt; der Cache wurde übergangen.
EXPIREDDie Kopie war abgelaufen; die Antwort wurde erneut vom Backend geholt.
STALEDas Backend konnte nicht antworten; eine abgelaufene Kopie wurde ausgeliefert.
UPDATINGWährend der Eintrag erneuert wurde, kam die alte Kopie zum Einsatz.
REVALIDATEDDas 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.

Nginx
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_valid von 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_path festgelegt haben.
  • Den Schlüssel ändern: Wenn Sie proxy_cache_key um einen Versionswert ergänzen und diesen ändern, werden alle alten Einträge auf einmal ungültig; die alten Dateien werden nach Ablauf der inactive-Frist von selbst entfernt.
  • Eine einzelne Adresse erneuern: Knüpfen Sie die Bedingung von proxy_cache_bypass an 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.
Bash
# 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 -delete

Eine 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/html oder Bildtypen in die Liste gzip_types aufnehmen.
  • proxy_cache fü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 location ein add_header schreiben 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 expires eine Dauer; eine lange Dauer nur bei versionierten Dateinamen.
  • gzip ist eingeschaltet; gzip_types enthält nur textbasierte Typen, dazu gzip_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_bypass und proxy_no_cache ausgenommen.
  • Verwaltungsbereich, Warenkorb und Kontoseiten laufen ohne Cache.
  • Mit X-Cache-Status wurden MISS und HIT beobachtet; angemeldet erscheint BYPASS.
  • Wie der Cache geleert wird, ist schriftlich festgehalten und erprobt.
  • Nach jeder Änderung wurde nginx -t ausgefü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

Sprechen wir über die Infrastruktur Ihrer Website

BYK Yazılım entwickelt Unternehmenswebsites. Schreiben Sie uns bei Fragen zu Ihrer Website.

Kontakt aufnehmen Unternehmenswebsite