Nginx Performance-Tuning: gzip, Cache und Connection-Pool-Konfiguration

Letzte Woche kam der Alarm: Die Startseite eines E-Commerce-Shops lud plötzlich in 4 Sekunden. Chrome DevTools geöffnet – HTML 120 KB, CSS plus JS weitere 350 KB, alles unkomprimiert. Schlimmer noch: Jeder Request ging ans Backend, Cache-Hit-Rate nur 12 %. Abends nach Feierabend 2 Stunden Nginx-Konfiguration angepasst: gzip aktiviert, Cache-Strategie ergänzt, Connection-Pool-Parameter optimiert. Am nächsten Morgen: Startseiten-Ladezeit auf 1,6 Sekunden, Backend-QPS fast halbiert.
Solche Probleme sind allzu häufig. Viele installieren Nginx und lassen es laufen – gzip standardmäßig aus, Cache nur halbherzig konfiguriert, Verbindungslimit auf Default. Bei Traffic-Spitzen gerät der Server außer Atem.
Dieser Artikel fasst die in der Produktion validierten Nginx Performance-Tuning-Konfigurationen zusammen. gzip reduziert das Übertragungsvolumen um 60–80 %, bei 95 % Cache-Hit-Rate sinkt die Backend-Last um 90 %, ein gut konfigurierter Connection-Pool vervier- bis verfünffacht die Parallelität. Konfigurationsdetails, Fallstricke und Messdaten werden ausführlich erklärt.
Kapitel 1: gzip-Komprimierung – Übertragungsvolumen reduzieren
Warum gzip so wichtig ist: Stellen Sie sich vor, Ihre HTML-Datei ist 100 KB groß – per gzip komprimiert sind es nur noch 20–25 KB. Die gesparten 75–80 KB Bandbreite bedeuten schnellere Ladezeiten für Nutzer und geringere Traffic-Kosten für Sie.
Bei einem Kundenprojekt wurde mir das bewusst: Über 60 % mobile Nutzer, viele im 4G-Netz. Startseite 3–4 Sekunden Ladezeit, Absprungrate 70 %. Mit gzip sank das Übertragungsvolumen um 70 %, First Paint auf ca. 1,5 Sekunden.
1.1 Grundkonfiguration: Erst zum Laufen bringen
Die gzip-Konfiguration in Nginx ist überschaubar:
http {
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
}
gzip on ist der Schalter. gzip_vary on ist entscheidend: Der Response-Header Vary: Accept-Encoding teilt CDN und Browser mit, dass sich der Inhalt je nach Komprimierungsfähigkeit des Clients unterscheidet – verhindert Cache-Fehler.
gzip_min_length 1000 bedeutet: Dateien unter 1 KB werden nicht komprimiert. Zu kleine Dateien bringen wenig Nutzen und kosten CPU. gzip_types legt die MIME-Typen fest; standardmäßig wird nur text/html komprimiert – CSS, JS, JSON, XML müssen explizit ergänzt werden.
1.2 Fortgeschritten: Kompressionslevel und MIME-Typen
Das Kompressionslevel erfordert Abwägung. gzip_comp_level reicht von 1–9: höhere Werte = bessere Kompression, mehr CPU-Verbrauch.
Testergebnisse:
| Kompressionslevel | HTML-Kompression | CPU-Zeit (ms) | Empfohlenes Szenario |
|---|---|---|---|
| 1 | 65 % | 2 | Knapp CPU |
| 4 | 72 % | 3 | Ausgewogen (empfohlen) |
| 6 | 75 % | 5 | Knapp Bandbreite (empfohlen) |
| 9 | 78 % | 12 | Extremszenario |
Level 4 und 6 sind in den meisten Fällen die beste Wahl. Level 9 verdoppelt den CPU-Verbrauch bei nur wenigen Prozentpunkten mehr Kompression – lohnt sich nicht.
Vollständige gzip-Konfiguration aus der Produktion:
# gzip-Komprimierung
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml
application/xml+rss
application/xhtml+xml
application/x-javascript;
gzip_disable "msie6";
gzip_proxied any wird oft übersehen. Wenn Nginx als Reverse Proxy arbeitet und das Backend keinen Content-Length-Header liefert, wird standardmäßig nicht komprimiert. Mit any werden alle passenden Responses komprimiert.
gzip_disable "msie6" dient der Kompatibilität mit altem IE6, der gzip fehlerhaft unterstützt. IE6 ist praktisch ausgestorben – die Zeile kann entfernt werden, ich lasse sie aus Vorsicht drin.
1.3 Welche Dateitypen profitieren am meisten?
Messdaten zeigen deutliche Unterschiede:
| Dateityp | Originalgröße | Nach Kompression | Kompressionsrate |
|---|---|---|---|
| HTML | 100 KB | 20–25 KB | 75–80 % |
| CSS | 80 KB | 24–28 KB | 65–70 % |
| JavaScript | 120 KB | 36–42 KB | 65–70 % |
| JSON API | 50 KB | 20–25 KB | 50–60 % |
| Bild/Video | bereits komprimiert | sinnlos | 0–5 % |
Bilder und Videos sind bereits komprimiert (JPEG, PNG, MP4) – erneutes gzip kann das Volumen sogar vergrößern. image/* und video/* gehören nicht in gzip_types.
Einmal half ich bei der Fehlersuche: In gzip_types stand image/jpeg – Bilder wurden 3–5 % größer. Solche Anfängerfehler habe ich selbst gemacht, als ich noch nicht wusste, dass nicht jeder MIME-Typ Sinn macht.
Kapitel 2: Cache-Strategie – statische Inhalte beschleunigen
Cache ist der direkteste Hebel beim Performance-Tuning. Richtig konfiguriert liefert Nginx 95 % der Requests direkt aus – ohne Backend-Anfrage. Zu oft läuft das Backend auf Hochtouren, während der Nginx-Cache ungenutzt bleibt – nicht weil er aus ist, sondern weil er falsch konfiguriert ist.
2.1 proxy_cache oder fastcgi_cache?
Nginx bietet zwei Cache-Mechanismen:
- proxy_cache: Cached Responses von Upstream-Servern – für Reverse-Proxy-Szenarien (Node.js, Python, Go)
- fastcgi_cache: Cached FastCGI-Prozess-Responses – für PHP-FPM
Die Wahl hängt vom Backend-Stack ab: PHP → fastcgi_cache; Node.js, Python, Go → proxy_cache. Die Konfigurationslogik ist nahezu identisch – im Folgenden am Beispiel von proxy_cache.
2.2 Vollständige proxy_cache-Konfiguration
Zuerst im http-Block den Cache-Pfad definieren:
http {
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=my_cache:10m
max_size=10g
inactive=60m
use_temp_path=off;
}
Zeile für Zeile:
levels=1:2: Verzeichnisstruktur mit zwei Ebenen – vermeidet zu viele Dateien in einem Ordnerkeys_zone=my_cache:10m: Cache-Name und Speicher für Metadaten – 10m reicht für ca. 80.000 Cache-Keysmax_size=10g: Obergrenze der Cache-Größe – Überschuss wird per LRU entferntinactive=60m: Einträge ohne Zugriff in 60 Minuten werden gelöschtuse_temp_path=off: Direktes Schreiben ins Cache-Verzeichnis – kein Temp-Datei-Overhead
Dann im server- oder location-Block aktivieren:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_cache my_cache;
# Cache-Gültigkeit
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_valid any 1m;
# Cache-Key-Design
proxy_cache_key $scheme$request_method$host$request_uri;
# Stale-Strategie (siehe unten)
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
# Cache-Status im Response-Header (Debug)
add_header X-Cache-Status $upstream_cache_status;
}
}
proxy_cache_valid definiert die Cache-Dauer je Statuscode:
200 302 10m: Erfolgreiche Responses 10 Minuten404 1m: 404-Fehler 1 Minute – schützt Backend vor Missbrauchany 1m: Andere Statuscodes 1 Minute
2.3 Cache-Key-Design und Invalidierung
Der Cache-Key proxy_cache_key bestimmt, wann zwei Requests als „gleich“ gelten. Default ist $scheme$proxy_host$request_uri – explizite Deklaration ist empfehlenswert:
proxy_cache_key $scheme$request_method$host$request_uri;
Der Key enthält Protokoll, HTTP-Methode, Hostname und vollständige URI – präziser bei GET/POST-Mix oder mehreren Domains.
Invalidierung ist knifflig. Gängige Strategien:
- Zeitbasiert:
proxy_cache_valid– automatisches Ablaufen - Aktives Bypass:
proxy_cache_bypassumgeht den Cache - Purge: Nginx Plus bietet
proxy_cache_purge
Ich nutze meist Variante 2 über Request-Header:
# Bestimmter Header umgeht Cache
proxy_cache_bypass $http_x_nocache;
# Oder Query-Parameter
proxy_cache_bypass $arg_nocache;
Cache refreshen: ?nocache=1 anhängen oder Header X-Nocache: 1 senden.
2.4 Stale-Strategie: Dienst auch bei Backend-Ausfall
proxy_cache_use_stale ist sehr praktisch: Bei Backend-Fehler oder Timeout liefert Nginx abgelaufene Cache-Inhalte statt eines Fehlers.
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
Beim letzten Singles’ Day hatten wir Backend-Probleme beim Skalieren – API intermittierend 502. Dank Stale-Strategie merkten Nutzer kaum etwas – Inhalt war ein paar Minuten alt, aber verfügbar. Nach Backend-Recovery aktualisierte sich der Cache automatisch.
Messdaten-Vergleich:
| Metrik | Ohne Cache | Cache-Hit | Stale-Modus |
|---|---|---|---|
| Antwortzeit | 150–200 ms | 5–10 ms | 5–10 ms |
| Backend-QPS | 1000 | 50 | 0 |
| Nutzererlebnis | Normal | Normal | Etwas langsamer |
Bei Cache-Hit sinkt die Antwortzeit von 200 ms auf 5–10 ms – fast 20-fach schneller. Der Nutzen ist unmittelbar sichtbar.
Kapitel 3: Connection-Pool – unverzichtbar bei hoher Parallelität
gzip und Cache lösen „schneller übertragen“ – der Connection-Pool löst „mehr Requests tragen“. Mit Default-Einstellungen hat ein Nginx-Worker maximal 1024 Verbindungen – bei Traffic-Spitzen reicht das nicht.
3.1 worker_connections: Obergrenze berechnen
Formel für maximale parallele Verbindungen:
Max. Parallelität = worker_processes × worker_connections
Beispiel: 8 CPU-Kerne, worker_processes auf 8 (oder auto), worker_connections auf 4096:
Max. Parallelität = 8 × 4096 = 32768
Die Zahl wirkt groß – aber jeder Request bindet typischerweise zwei Verbindungen (Client→Nginx, Nginx→Backend). Tatsächlich verarbeitbare parallele Requests: etwa die Hälfte.
Konfiguration im events-Block:
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
use epoll ist unter Linux Default – explizit setzen schadet nicht. multi_accept on lässt Worker mehrere neue Verbindungen gleichzeitig annehmen – reduziert Warteschlangen bei hoher Last.
3.2 Client-Keepalive: Verbindungen wiederverwenden
TCP-Verbindungsaufbau kostet Zeit (Three-Way-Handshake). Keepalive erlaubt Wiederverwendung zwischen Client und Nginx.
http {
keepalive_timeout 65;
keepalive_requests 1000;
}
keepalive_timeout 65: Verbindung bleibt 65 Sekunden offen. Zu lang bindet Ressourcen, zu kurz schwächt den Wiederverwendungseffekt. 60–75 Sekunden ist ein sinnvoller Bereich.
keepalive_requests 1000: Eine Verbindung verarbeitet maximal 1000 Requests. Zu niedrig = häufiges Trennen, zu hoch = Risiko für Ressourcenlecks. 1000 hat sich in Tests bewährt.
3.3 upstream keepalive: Backend-Connection-Pool
Viele kennen diese Konfiguration nicht – der Effekt ist deutlich. Nginx kann auch Verbindungen zum Backend wiederverwenden und spart TCP-Aufbau.
upstream backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
keepalive 64;
keepalive_timeout 60s;
keepalive_requests 1000;
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
keepalive 64: 64 idle Verbindungen im Pool – typisch 4–8× Anzahl der Backend-Server.
proxy_http_version 1.1 und proxy_set_header Connection "" sind Pflicht. HTTP/1.1 unterstützt keepalive; leerer Connection-Header ermöglicht Wiederverwendung. Ohne diese Zeilen greift upstream keepalive nicht.
Messdaten-Vergleich:
| Konfiguration | Verbindungsaufbauten/Min. | CPU-Overhead | Empfohlenes Szenario |
|---|---|---|---|
| Ohne upstream keepalive | 6000 | Hoch | Niedriger Traffic |
| keepalive 32 | 3000 | Mittel | Mittlerer Traffic |
| keepalive 64 | 1500 | Niedrig | Hoher Traffic |
Mit upstream keepalive sinken Verbindungsaufbauten um 50 % – besonders wichtig bei hoher Parallelität.
3.4 Parameter-Referenz nach Szenario
Empfohlene Werte je Traffic-Stufe:
| Parameter | Niedriger Traffic (<1000 QPS) | Mittlerer Traffic (1000–5000 QPS) | Hoher Traffic (>5000 QPS) |
|---|---|---|---|
| worker_processes | auto | auto | auto |
| worker_connections | 1024 | 2048 | 4096 |
| keepalive_timeout | 60 | 65 | 75 |
| keepalive_requests | 100 | 500 | 1000 |
| upstream keepalive | 16 | 32 | 64 |
Das ist der Ausgangspunkt – Feintuning braucht Lasttests. Ich nutze wrk oder ab und beobachte Verbindungszahlen und Antwortzeit-Kurven.
Kapitel 4: Gesamtvorlage – produktionsreif
Die ersten drei Kapitel erklären die Prinzipien – hier eine integrierte Vorlage. Parameter anpassen, Grundgerüst ist universell.
# nginx.conf – Produktionsvorlage
user nginx;
worker_processes auto;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
# gzip-Komprimierung
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml
application/xml+rss application/xhtml+xml;
gzip_disable "msie6";
# Cache-Pfad
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=my_cache:10m
max_size=10g
inactive=60m
use_temp_path=off;
# Client-Verbindungen
keepalive_timeout 65;
keepalive_requests 1000;
# Backend-Servergruppe
upstream backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
keepalive 64;
keepalive_timeout 60s;
keepalive_requests 1000;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
# Cache
proxy_cache my_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_key $scheme$request_method$host$request_uri;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
# Debug-Header
add_header X-Cache-Status $upstream_cache_status;
}
}
}
Differenzierte Konfiguration nach Szenario
E-Commerce: Startseite und Produktdetails ändern sich häufig – Cache 10–15 Minuten. upstream keepalive groß wählen, da DB-Queries das Backend belasten.
API-Dienste: Hohe Aktualität – proxy_cache_valid nur 1–5 Minuten. gzip wirkt gut auf JSON – unbedingt aktivieren.
Statische Sites: HTML, CSS, JS ändern sich selten – Cache 1 Stunde oder länger. gzip bringt hier den größten Nutzen.
Kapitel 5: Häufige Probleme und Fehlersuche
Typische Probleme aus der Praxis – direkt mit Lösung.
F: gzip greift nicht, Response-Header ohne Content-Encoding: gzip
Drei Stellen prüfen:
gzip onim richtigen Block (http)gzip_typesenthält den Response-MIME-Typ- Response-Größe über
gzip_min_length
Mit curl testen: curl -H "Accept-Encoding: gzip" -I http://your-site.com
F: Niedrige Cache-Hit-Rate, X-Cache-Status meist MISS
Häufige Ursachen:
- Cache-Key ungünstig – jeder Request gilt als „anders“
proxy_cache_validzu kurz- Backend liefert
Cache-Control: no-cacheoderSet-Cookie
Response-Header prüfen, ob Caching verboten wird.
F: worker_connections reicht nicht, 502-Fehler
Nginx-Fehlerlog prüfen – bei worker_connections are not enough ist die Parallelität überschritten.
Lösungen:
worker_connectionserhöhen- Connection-Leaks prüfen (Keepalive-Einstellungen)
- Lastverteilung auf mehrere Server
F: Hoher Speicherverbrauch, häufiges OOM
Mögliche Ursachen:
keys_zoneinproxy_cache_pathzu groß- Zu viele Cache-Dateien, hoher Memory-Mapping-Verbrauch
- Keepalive-Pool zu groß, viele idle Verbindungen
Parameter reduzieren oder mehr RAM bereitstellen.
Abschluss
Die drei Säulen des Nginx Performance-Tunings: gzip-Komprimierung, Cache-Strategie, Connection-Pool. Richtig kombiniert verdoppeln Sie die Ladegeschwindigkeit und vervier- bis verfünffachen die Parallelität.
Tuning ist kein Einmal-Projekt. Empfohlene Reihenfolge:
- gzip zuerst: Minimaler Aufwand, maximaler Nutzen – in zehn Minuten erledigt
- Cache danach: Nach Geschäftsszenario – innerhalb eines Tages umsetzbar
- Connection-Pool zuletzt: Lasttest nötig – wenn Traffic stabil ist
Nach jeder Änderung Lasttest: wrk oder ab, Antwortzeit, QPS, Fehlerrate beobachten. Nicht nach Gefühl, sondern nach Daten.
Checkliste zum Abhaken:
- gzip aktiv, MIME-Typen vollständig
- gzip_comp_level 4–6, CPU und Kompression ausbalanciert
- proxy_cache_path konfiguriert, Cache-Größe sinnvoll
- proxy_cache_valid nach Geschäftsszenario
- proxy_cache_use_stale Stale-Strategie aktiv
- worker_connections 4096 oder höher
- keepalive_timeout 60–75 Sekunden
- upstream keepalive inkl. HTTP/1.1 und Connection-Header
- X-Cache-Status-Header für Debugging
Das war’s. Bei Fragen gerne in den Kommentaren – ich antworte, wenn ich kann.
Nginx Performance-Tuning-Konfigurationsablauf
Drei Schritte für gzip-Komprimierung, Cache-Strategie und Connection-Pool-Optimierung in der Produktion
⏱️ Estimated time: 30 min
- 1
Step 1: gzip-Komprimierung aktivieren
Im http-Block konfigurieren:
• gzip on; Komprimierung aktivieren
• gzip_vary on; Vary-Response-Header setzen
• gzip_comp_level 6; Kompressionslevel festlegen (empfohlen 4–6)
• gzip_min_length 1000; Dateien unter 1 KB nicht komprimieren
• gzip_types MIME-Typen angeben: text/plain text/css application/json application/javascript - 2
Step 2: proxy_cache konfigurieren
Zweistufige Konfiguration:
Schritt 1: Cache-Pfad definieren
• proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m
Schritt 2: Cache im location-Block aktivieren
• proxy_cache my_cache;
• proxy_cache_valid 200 10m; Erfolgreiche Responses 10 Minuten cachen
• proxy_cache_use_stale error timeout http_502; Stale-Strategie konfigurieren - 3
Step 3: Connection-Pool-Parameter optimieren
Drei zentrale Einstellungen:
• worker_connections 4096; im events-Block setzen
• keepalive_timeout 65; keepalive_requests 1000; im http-Block setzen
• upstream keepalive 64; proxy_http_version 1.1; proxy_set_header Connection ""; Backend-Connection-Pool
FAQ
Welches gzip-Kompressionslevel ist sinnvoll?
proxy_cache oder fastcgi_cache – wie wählen?
Welchen Wert soll worker_connections haben?
Niedrige Cache-Hit-Rate – wie debuggen?
Warum muss upstream keepalive HTTP/1.1 nutzen?
Wie Cache-Zeiten für verschiedene Geschäftsszenarien setzen?
9 Min. Lesezeit · Veröffentlicht am: 11. Apr. 2026 · Aktualisiert am: 14. Juli 2026
Nginx Praxisleitfaden
Wenn du über die Suche hier gelandet bist, kommst du am schnellsten weiter, indem du zum vorherigen oder nächsten Beitrag dieser Serie springst.
Vorheriger
Nginx Reverse Proxy: upstream, Buffer und Timeouts im Überblick
Ausführliche Anleitung zu den drei Kernmodulen des Nginx Reverse Proxy: upstream-Lastverteilung, proxy_buffer-Tuning und Timeout-Konfiguration – inklusive 502/504-Fehleranalyse, keepalive-Connection-Pool und Health Checks
Teil 1 von 6
Nächster
Nginx SSL/TLS in der Praxis: von HTTPS-Zertifikaten bis A+ Sicherheitshärtung
Nginx HTTPS von Grund auf: Let's Encrypt-Zertifikate, TLS-1.3-Härtung, SSL-Labs-A+-Konfigurationsvorlage, OCSP-Stapling-Performance-Optimierung – vollständiger Leitfaden Stand 2026.
Teil 3 von 6
Ähnliche Beiträge
Nginx Lastverteilung in der Praxis: upstream-Konfiguration und Health Checks

Nginx Lastverteilung in der Praxis: upstream-Konfiguration und Health Checks
Nginx dynamischer Upstream: Echtzeit-Service-Discovery mit Lua


Kommentare
Melde dich mit GitHub an, um einen Kommentar zu hinterlassen