Nginx Performance-Tuning in der Praxis: 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 %, Brotli spart weitere 15–25 %; bei 95 % Cache-Hit-Rate sinkt die Backend-Last um 90 %; ein gut konfigurierter Connection-Pool vervier- bis verfünffacht die Parallelität; mit Thread Pools und reuseport steigt der RPS pro Server auf 50K–80K. Konfigurationsdetails, Fallstricke und Messdaten werden ausführlich erklärt.
Kapitel 1: gzip/Brotli-Komprimierung – Übertragungsvolumen reduzieren
Warum gzip so wichtig ist: Eine HTML-Datei von 100 KB wird per gzip auf 20–25 KB komprimiert. 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 gzip-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 gzip-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-Kompressionsrate | CPU-Zeit (ms) | Empfohlenes Szenario |
|---|---|---|---|
| 1 | 65 % | 2 | CPU knapp |
| 4 | 72 % | 3 | Ausgewogen (empfohlen) |
| 6 | 75 % | 5 | Bandbreite knapp (empfohlen) |
| 9 | 78 % | 12 | Extremszenario |
Level 4 und 6 sind in den meisten Fällen optimal. Level 9 verdoppelt den CPU-Verbrauch bei nur wenigen Prozentpunkten mehr Kompression – selten lohnenswert.
Produktionskonfiguration:
# 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. Als Reverse Proxy komprimiert Nginx standardmäßig nicht, wenn das Backend keinen Content-Length-Header liefert. any erzwingt Komprimierung aller passenden Responses.
gzip_disable "msie6" dient der IE6-Kompatibilität. 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 Komprimierung | 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 (JPEG, PNG, MP4) sind bereits komprimiert – gzip kann das Volumen sogar vergrößern. image/* und video/* gehören nicht in gzip_types.
Einmal half ich bei der Fehlersuche: gzip_types enthielt image/jpeg, Bilder wuchsen um 3–5 %. Solche Fehler habe ich selbst auch schon gemacht – früher wollte man alle MIME-Typen hinzufügen.
1.4 Brotli: 15–25 % mehr als gzip
Brotli (Google) liefert bei gleichem Level 15–25 % bessere Kompression als gzip – ideal für statische Ressourcen, da Browser-Unterstützung weit verbreitet ist.
Achtung: Brotli ist kein Standardmodul – zusätzliche Kompilierung oder dynamisches Modul nötig. Mit offiziellen dynamischen Modulen ohne Nginx-Neukompilierung:
# Modul laden (bei dynamischem Modul)
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
http {
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/javascript application/json;
brotli_min_length 256;
}
Brotli-Level 1–11; Level 6 empfohlen. Sehr hohe Level (z. B. 11) verlängern die Kompressionszeit für dynamische Inhalte deutlich. Statische Dateien können vorab mit maximalem Level komprimiert werden, Nginx liefert die .br-Datei direkt aus.
Vergleichsmessungen:
| Komprimierung | 100 KB HTML danach | Kompressionszeit | Browser-Unterstützung |
|---|---|---|---|
| gzip (Level 6) | 25 KB | 5 ms | fast alle |
| Brotli (Level 6) | 18 KB | 15 ms | 95 %+ |
| Brotli (Vorab Level 11) | 15 KB | 0 | 95 %+ |
Empfehlung: Dynamische Inhalte Brotli Level 4–6, statische Ressourcen Vorabkomprimierung. Bei komplizierter Nginx-Installation reicht gzip – 75 % Kompression ist bereits beachtlich.
Kapitel 2: Cache-Strategie – Statische Inhalte beschleunigen
Cache liefert den direktesten Performance-Gewinn. Bei guter Konfiguration bedient Nginx 95 % der Requests direkt, ohne Backend-Kontakt. Zu oft läuft das Backend heiß, während der Nginx-Cache praktisch ungenutzt bleibt – nicht weil er fehlt, sondern weil er falsch konfiguriert ist.
2.1 proxy_cache oder fastcgi_cache?
Zwei Cache-Mechanismen in Nginx:
- proxy_cache: Responses von Upstream-Servern, für Reverse-Proxy (Node.js, Python, Go)
- fastcgi_cache: FastCGI-Responses, für PHP-FPM
Je nach Backend-Stack: PHP → fastcgi_cache; Node.js, Python, Go → proxy_cache. Die Logik ist nahezu identisch – im Folgenden proxy_cache als Beispiel.
2.2 Vollständige proxy_cache-Konfiguration
Zuerst Cache-Pfad im http-Block:
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: Zwei Verzeichnisebenen, vermeidet zu viele Dateien in einem Ordnerkeys_zone=my_cache:10m: Cache-Name und Metadaten-Speicher, 10m ≈ 80.000 Cache-Keysmax_size=10g: Obergrenze, LRU-Eviction bei Überschreitunginactive=60m: Einträge ohne Zugriff in 60 Minuten werden gelöschtuse_temp_path=off: Direktes Schreiben ins Cache-Verzeichnis, kein Temp-Datei-Overhead
Aktivierung in server oder location:
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
proxy_cache_key $scheme$request_method$host$request_uri;
# Degradationsstrategie
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 pro Statuscode:
200 302 10m: Erfolgreiche Responses 10 Minuten404 1m: 404-Fehler 1 Minute – schützt vor Missbrauchany 1m: Andere Statuscodes 1 Minute
2.3 Cache-Key-Design und Invalidierung
Der Cache-Key $scheme$proxy_host$request_uri ist der Default – explizite Deklaration empfohlen:
proxy_cache_key $scheme$request_method$host$request_uri;
Enthält Protokoll, HTTP-Methode, Hostname und vollständige URI. Bei GET/POST-Mix oder mehreren Domains präziser.
Invalidierungsstrategien:
- Zeitablauf:
proxy_cache_valid - Aktives Bypass:
proxy_cache_bypass - Cache-Purge: Nginx Plus mit
proxy_cache_purge
Meist Variante 2 per Request-Header:
# Bestimmter Header umgeht Cache
proxy_cache_bypass $http_x_nocache;
# Oder per Query-Parameter
proxy_cache_bypass $arg_nocache;
Cache-Refresh: ?nocache=1 oder Header X-Nocache: 1.
2.4 Degradation: Service auch bei Backend-Ausfall
proxy_cache_use_stale liefert abgelaufenen Cache bei Backend-Fehler oder Timeout statt direktem Fehler.
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
Beim letzten Singles’ Day hatten wir intermittierende 502-Fehler beim Backend-Scaling. Dank Degradation blieb der Nutzerzugriff stabil – Inhalt ein paar Minuten veraltet, aber verfügbar. Nach Backend-Erholung aktualisierte sich der Cache automatisch.
Messdaten:
| Metrik | Ohne Cache | Cache-Hit | Degradationsmodus |
|---|---|---|---|
| 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.
2.5 Microcaching: Dynamische Inhalte beschleunigen
Dynamische Inhalte können per Microcaching (1–5 Sekunden) gecacht werden – bei Spitzenlast deutlich weniger Backend-Druck.
Bei einem E-Commerce-Shop: Startseite mit Echtzeit-Empfehlungen und Lagerbestand, vollständig dynamisch. Beim großen Sale explodierte der Traffic. Lösung: 5-Sekunden-Microcache:
proxy_cache_path /var/cache/nginx/micro levels=1:2 keys_zone=micro:10m max_size=1g;
location / {
proxy_cache micro;
proxy_cache_valid 200 5s; # nur 5 Sekunden
proxy_cache_lock on; # verhindert Cache-Stampede
proxy_cache_background_update on; # asynchrones Cache-Update
}
proxy_cache_lock on ist entscheidend: Bei abgelaufenem Cache holt der erste Request neue Daten, andere warten auf den alten Cache – kein gleichzeitiger Backend-Ansturm.
Ergebnis: Startseiten-TTFB von 800 ms auf ca. 5 ms, Backend-QPS von 2000 auf 400. 5 Sekunden Verzögerung sind für Nutzer kaum spürbar, Serverlast deutlich geringer.
2.6 Bedingte Requests: Bandbreite sparen
proxy_cache_revalidate nutzt bedingte Requests (If-Modified-Since / If-None-Match) zur Validierung. Bei 304 Not Modified wird kein vollständiger Inhalt übertragen, nur Metadaten aktualisiert.
proxy_cache_revalidate on;
Besonders nützlich bei großen Dateien mit seltenen Änderungen.
Kapitel 3: Connection-Pool – Pflicht bei hoher Parallelität
gzip und Cache beschleunigen die Übertragung; der Connection-Pool trägt mehr gleichzeitige Requests. Standardmäßig maximal 1024 Verbindungen pro Nginx-Worker – 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
Bei 8 CPU-Kernen, worker_processes 8 (oder auto), worker_connections 4096:
Max. Parallelität = 8 × 4096 = 32768
Wichtig: Jeder Request nutzt typischerweise zwei Verbindungen (Client→Nginx, Nginx→Backend). Tatsächliche parallele Requests ≈ Hälfte dieses Werts.
Konfiguration im events-Block:
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
use epoll ist unter Linux Standard, explizite Angabe schadet nicht. multi_accept on akzeptiert mehrere neue Verbindungen gleichzeitig – weniger Warteschlangen bei hoher Last.
3.2 Client-Keepalive: Verbindungen wiederverwenden
TCP-Verbindungsaufbau (Three-Way-Handshake) kostet Ressourcen. Keepalive ermöglicht Wiederverwendung zwischen Client und Nginx.
http {
keepalive_timeout 65;
keepalive_requests 1000;
}
keepalive_timeout 65: Verbindung 65 Sekunden offen, dann geschlossen. Nicht zu groß (Ressourcenbindung), nicht zu klein (wenig Wiederverwendung). 60–75 Sekunden sind sinnvoll.
keepalive_requests 1000: Maximal 1000 Requests pro Verbindung. 1000 ist ein erprobter Wert.
Neuer Parameter keepalive_time – maximale Lebensdauer unabhängig von Request-Anzahl:
keepalive_time 1h; # Verbindung max. 1 Stunde
3.3 Upstream-Keepalive: Backend-Connection-Pool
Viele kennen diese Konfiguration nicht – der Effekt ist deutlich. Nginx und Backend können Verbindungen wiederverwenden und TCP-Overhead sparen.
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 – ca. 4–8× Backend-Server-Anzahl.
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.
Messvergleich:
| 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 |
Upstream keepalive halbiert den Verbindungsaufbau-Overhead – besonders wichtig bei hoher Parallelität.
3.4 Dateideskriptor-Limits: Systemebene nicht vergessen
Nginx-Verbindungen sind durch Dateideskriptor-Limits gebunden. worker_connections 4096 nützt wenig, wenn das System nur 1024 Dateien pro Prozess erlaubt.
Aktuelles Limit prüfen:
ulimit -n
Bei Wert unter 65536 in /etc/security/limits.conf anheben:
* soft nofile 65536
* hard nofile 65536
In der Nginx-Konfiguration:
worker_rlimit_nofile 65536;
Im main-Block (neben worker_processes), damit Nginx beim Start genug Deskriptoren anfordert.
3.5 Parameter-Übersicht nach Szenario
| 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 |
| worker_rlimit_nofile | 4096 | 8192 | 65536 |
Ausgangspunkt – Anpassung per Lasttest mit wrk oder ab anhand von Verbindungszahl und Antwortzeit.
Kapitel 4: Fortgeschrittene Optimierung – Thread Pools und reuseport
Die ersten drei Kapitel decken die meisten Szenarien ab. Bei sehr hohem Traffic (RPS >50K pro Server) oder latenzkritischen Anwendungen zwei weitere Optionen:
4.1 Thread Pools: sendfile-Engpass überwinden
Nginx nutzt standardmäßig ein einthreadiges Event-Modell – für die meisten Fälle effizient. Bei statischen Dateien mit hoher Parallelität: sendfile ist Zero-Copy, aber Dateilesen und Socket-Schreiben blockieren im selben Worker-Thread. Langsame Disk-I/O blockiert den gesamten Worker.
Thread Pools trennen Dateilesen und Senden in einen separaten Pool – der Worker koordiniert nur.
Nginx-Blog-Test: 9-fache Performance-Steigerung bei 1-MB-Downloads, begrenzt durch Disk-I/O.
Konfiguration:
http {
thread_pool default threads=32 max_queue=65536;
aio threads=default;
sendfile_max_chunk 512k;
}
threads=32: 32 Threads im Pool, max_queue=65536 maximale Warteschlange. aio threads=default aktiviert asynchrones I/O. sendfile_max_chunk 512k begrenzt die Chunk-Größe pro Sendevorgang.
Nicht universell: Bei dynamischen API-Services ist Disk-I/O selten der Engpass – Thread Pools können Overhead durch Context-Switch erhöhen. Empfehlung: statische Dateien, große Downloads.
4.2 Socket Sharding (reuseport): Verbindungslatenz senken
Seit Nginx 1.9.1 kann reuseport mehrere Worker unabhängig auf demselben Port lauschen lassen – ohne Lock-Konkurrenz.
Traditionell teilen sich alle Worker einen Listen-Socket; neue Verbindungen konkurrieren um den accept-Mutex – Latenz-Jitter bei hoher Last.
Mit reuseport:
server {
listen 80 reuseport;
}
Jeder Worker hat einen eigenen Listen-Socket, der Kernel verteilt Verbindungen – keine Lock-Konkurrenz.
Messdaten (Nginx-Blog):
| Metrik | ohne reuseport | mit reuseport |
|---|---|---|
| Durchschnittslatenz | 15,65 ms | 12,35 ms |
| Latenz-Standardabweichung | 3,5 ms | 1,2 ms |
| Verbindungsverteilung | ungleichmäßig | gleichmäßig |
21 % weniger Latenz, deutlich geringerer Jitter. Besonders ab QPS >20K spürbar.
4.3 open_file_cache: Dateideskriptor-Cache
Für statische Dateien: open_file_cache cached Dateideskriptoren und Metadaten – kein wiederholtes Disk-Lookup pro Request.
http {
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
}
max=10000: Maximal 10.000 Datei-Infosinactive=30s: 30 Sekunden ohne Zugriff → Löschungopen_file_cache_valid 60s: Validierung alle 60 Sekundenopen_file_cache_errors on: Auch Fehlerzustände (Datei nicht gefunden) cachen
Effektiv für statische Sites. Bei dynamischen Inhalten nicht empfohlen – häufige Änderungen führen zu veralteten Inhalten.
Kapitel 5: Gesamtkonfigurationsvorlage – Produktionstauglich
Nach den vier Kapiteln mit Theorie folgt eine integrierte Vorlage. Parameter je nach Szenario anpassbar, Grundgerüst universell.
# nginx.conf Produktionsvorlage (hoher Traffic)
user nginx;
worker_processes auto;
worker_rlimit_nofile 65536;
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;
keepalive_time 1h;
# Datei-Cache (optional für statische Sites)
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
# 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 reuseport;
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;
proxy_cache_revalidate on;
# Debug-Response-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. Großes upstream keepalive wegen DB-Last. Microcaching (1–3 Sekunden) bei Sales.
API-Service: Hohe Echtzeitanforderungen – proxy_cache_valid 1–5 Minuten. gzip für JSON unbedingt aktivieren. Brotli wenn möglich – kleine Responses, aber hohe Request-Frequenz.
Statische Site: HTML, CSS, JS ändern sich selten – Cache 1 Stunde oder länger. gzip maximal effektiv bei Textdateien. open_file_cache und Thread Pools sinnvoll.
Performance-Vergleich (aktualisiert)
| Konfiguration | Vor Optimierung | Nach Optimierung | Verbesserung |
|---|---|---|---|
| HTML-Übertragungsvolumen (gzip) | 100 KB | 25 KB | 75 % ↓ |
| HTML-Übertragungsvolumen (Brotli) | 100 KB | 18 KB | 82 % ↓ |
| API-Antwortzeit (Cache-Hit) | 200 ms | 8 ms | 96 % ↓ |
| Parallele Verbindungen | 1000 | 4000 | 4× ↑ |
| RPS (optimiert pro Server) | 10K | 50K–80K | 5–8× ↑ |
| TTFB (reuseport) | 15,65 ms | 12,35 ms | 21 % ↓ |
Kombination aus eigenen Messungen und offizieller Dokumentation. Tatsächlicher Effekt hängt von Hardware, Netzwerk und Workload ab – Lasttest unverzichtbar.
Kapitel 6: Häufige Probleme und Fehlerbehebung
Häufige Probleme mit direkten Lösungen.
F: gzip greift nicht, kein Content-Encoding: gzip im Response-Header
Drei Stellen prüfen:
gzip onim richtigen Block (http)gzip_typesenthält den Response-MIME-Typ- Response-Größe über
gzip_min_length
Test: curl -H "Accept-Encoding: gzip" -I http://your-site.com
F: Niedrige Cache-Hit-Rate, X-Cache-Status meist MISS
Häufige Ursachen:
- Ungünstiger Cache-Key – jeder Request gilt als „anders“
proxy_cache_validzu kurz- Backend liefert
Cache-Control: no-cacheoderSet-Cookie
Response-Header auf Caching-Verbot prüfen.
F: worker_connections reicht nicht, 502-Fehler
Nginx-Fehlerlog: worker_connections are not enough = Parallelität überschritten.
Lösungen:
worker_connectionserhöhen- Connection-Leaks prüfen (keepalive-Einstellungen)
- Mehr Server für Lastverteilung
F: Konflikt zwischen gzip und sendfile?
Kein Konflikt. gzip komprimiert den Inhalt, sendfile steuert die Übertragungsart. Beides parallel möglich. gzip bei dynamischen Inhalten komprimiert im Speicher (kein sendfile); vorab komprimierte statische Dateien können per sendfile ausgeliefert werden.
F: upstream keepalive greift nicht?
Zwei typische Fehler:
proxy_http_version 1.1fehltproxy_set_header Connection ""fehlt
Beide Zeilen im location-Block, nicht im upstream-Block.
F: reuseport-Fehler „duplicate listen options“?
reuseport nur einmal pro listen-Zeile. Bei mehreren server-Blöcken auf demselben Port: jeder braucht eigenes reuseport.
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ß
Parameter reduzieren oder mehr RAM.
Abschluss
Nginx Performance-Tuning läuft im Kern auf drei Dinge hinaus: Komprimierung, Cache, Connection-Pool. Mit Brotli, Microcaching, Thread Pools und reuseport steigt der RPS pro Server von 10K auf 50K–80K.
Tuning ist kein einmaliges Projekt. Empfohlene Reihenfolge:
- gzip aktivieren: Minimaler Aufwand, maximaler Nutzen – zehn Minuten
- Cache konfigurieren: Nach Business-Szenario, innerhalb eines Tages
- Connection-Pool optimieren: Mit Lasttest validieren, bei stabilem Traffic
- Fortgeschrittene Optimierung: Thread Pools und reuseport erst bei sehr hohem Traffic
Nach jeder Änderung Lasttest mit wrk oder ab – Antwortzeit, QPS, Fehlerrate messen. Nicht nach Gefühl, sondern nach Daten.
Checkliste zum Abhaken:
- gzip aktiv, MIME-Typen vollständig
- gzip_comp_level 4–6
- Brotli konfiguriert (falls verfügbar)
- proxy_cache_path mit sinnvoller Größe
- proxy_cache_valid passend zum Szenario
- Microcaching für dynamische Inhalte bei hoher Last
- proxy_cache_use_stale als Degradation
- worker_connections ≥4096
- keepalive_timeout 60–75 Sekunden
- upstream keepalive inkl. HTTP/1.1 und Connection-Header
- reuseport bei hohem Traffic
- Dateideskriptor-Limit auf 65536
- X-Cache-Status-Header für Debugging
Bei Fragen gerne in den Kommentaren – ich antworte, wenn möglich.
FAQ
gzip-Komprimierung greift nicht, Response-Header ohne Content-Encoding: gzip – was tun?
Niedrige Cache-Hit-Rate, X-Cache-Status meist MISS – wie debuggen?
worker_connections reicht nicht, 502-Fehler – wie lösen?
Warum greift upstream keepalive oft nicht?
Brotli oder gzip – wie wählen?
Empfohlene Parameter für verschiedene Traffic-Szenarien?
12 Min. Lesezeit · Veröffentlicht am: 15. Mai 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 dynamischer Upstream: Echtzeit-Service-Discovery mit Lua
OpenResty dynamischer Upstream in drei Schichten: Health-Check-Vergleich, Consul/Nacos/etcd-Integrationscode für Echtzeit-Service-Discovery in Container-Umgebungen.
Teil 5 von 6
Nächster
Dies ist bisher der neueste Beitrag dieser Serie.
Ähnliche Beiträge
Nginx Reverse Proxy: upstream, Buffer und Timeouts im Überblick

Nginx Reverse Proxy: upstream, Buffer und Timeouts im Überblick
Nginx Performance-Tuning: gzip, Cache und Connection-Pool-Konfiguration


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