Design wechseln

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

Easton editorial illustration: before-and-after response pipe, gzip compression module, cache chamber, connection pool manifold

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:

KompressionslevelHTML-KompressionsrateCPU-Zeit (ms)Empfohlenes Szenario
165 %2CPU knapp
472 %3Ausgewogen (empfohlen)
675 %5Bandbreite knapp (empfohlen)
978 %12Extremszenario

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:

DateitypOriginalgrößeNach KomprimierungKompressionsrate
HTML100 KB20–25 KB75–80 %
CSS80 KB24–28 KB65–70 %
JavaScript120 KB36–42 KB65–70 %
JSON API50 KB20–25 KB50–60 %
Bild/Videobereits komprimiertsinnlos0–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:

Komprimierung100 KB HTML danachKompressionszeitBrowser-Unterstützung
gzip (Level 6)25 KB5 msfast alle
Brotli (Level 6)18 KB15 ms95 %+
Brotli (Vorab Level 11)15 KB095 %+

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 Ordner
  • keys_zone=my_cache:10m: Cache-Name und Metadaten-Speicher, 10m ≈ 80.000 Cache-Keys
  • max_size=10g: Obergrenze, LRU-Eviction bei Überschreitung
  • inactive=60m: Einträge ohne Zugriff in 60 Minuten werden gelöscht
  • use_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 Minuten
  • 404 1m: 404-Fehler 1 Minute – schützt vor Missbrauch
  • any 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:

  1. Zeitablauf: proxy_cache_valid
  2. Aktives Bypass: proxy_cache_bypass
  3. 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:

MetrikOhne CacheCache-HitDegradationsmodus
Antwortzeit150–200 ms5–10 ms5–10 ms
Backend-QPS1000500
Nutzererlebnisnormalnormaletwas 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:

KonfigurationVerbindungsaufbauten/Min.CPU-OverheadEmpfohlenes Szenario
ohne upstream keepalive6000hochniedriger Traffic
keepalive 323000mittelmittlerer Traffic
keepalive 641500niedrighoher 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

ParameterNiedriger Traffic (<1000 QPS)Mittlerer Traffic (1000–5000 QPS)Hoher Traffic (>5000 QPS)
worker_processesautoautoauto
worker_connections102420484096
keepalive_timeout606575
keepalive_requests1005001000
upstream keepalive163264
worker_rlimit_nofile4096819265536

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):

Metrikohne reuseportmit reuseport
Durchschnittslatenz15,65 ms12,35 ms
Latenz-Standardabweichung3,5 ms1,2 ms
Verbindungsverteilungungleichmäßiggleichmäß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-Infos
  • inactive=30s: 30 Sekunden ohne Zugriff → Löschung
  • open_file_cache_valid 60s: Validierung alle 60 Sekunden
  • open_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)

KonfigurationVor OptimierungNach OptimierungVerbesserung
HTML-Übertragungsvolumen (gzip)100 KB25 KB75 % ↓
HTML-Übertragungsvolumen (Brotli)100 KB18 KB82 % ↓
API-Antwortzeit (Cache-Hit)200 ms8 ms96 % ↓
Parallele Verbindungen100040004× ↑
RPS (optimiert pro Server)10K50K–80K5–8× ↑
TTFB (reuseport)15,65 ms12,35 ms21 % ↓

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:

  1. gzip on im richtigen Block (http)
  2. gzip_types enthält den Response-MIME-Typ
  3. 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_valid zu kurz
  • Backend liefert Cache-Control: no-cache oder Set-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:

  1. worker_connections erhöhen
  2. Connection-Leaks prüfen (keepalive-Einstellungen)
  3. 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:

  1. proxy_http_version 1.1 fehlt
  2. proxy_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_zone in proxy_cache_path zu 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:

  1. gzip aktivieren: Minimaler Aufwand, maximaler Nutzen – zehn Minuten
  2. Cache konfigurieren: Nach Business-Szenario, innerhalb eines Tages
  3. Connection-Pool optimieren: Mit Lasttest validieren, bei stabilem Traffic
  4. 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?
Drei Stellen prüfen: 1) gzip on im http-Block; 2) gzip_types enthält den Response-MIME-Typ; 3) Response-Größe über gzip_min_length. Mit curl -H "Accept-Encoding: gzip" -I testen.
Niedrige Cache-Hit-Rate, X-Cache-Status meist MISS – wie debuggen?
Häufige Ursachen: ungünstiger Cache-Key, proxy_cache_valid zu kurz, Backend liefert Cache-Control: no-cache oder Set-Cookie. Response-Header prüfen, ob Caching verboten wird.
worker_connections reicht nicht, 502-Fehler – wie lösen?
Nginx-Fehlerlog prüfen; bei „worker_connections are not enough“ ist die Parallelität überschritten. Lösung: worker_connections erhöhen, Connection-Leaks prüfen oder Lastverteilung auf mehrere Server.
Warum greift upstream keepalive oft nicht?
Zwei typische Fehler: 1) proxy_http_version 1.1 fehlt – HTTP/1.0 unterstützt keepalive standardmäßig nicht; 2) proxy_set_header Connection "" fehlt. Beide Zeilen müssen im location-Block stehen.
Brotli oder gzip – wie wählen?
Brotli komprimiert 15–25 % besser als gzip, erfordert aber ein zusätzliches Modul. Dynamische Inhalte: Brotli Level 4–6, statische Ressourcen: Vorabkomprimierung Level 11. Bei komplizierter Nginx-Kompilierung reicht gzip Level 6 mit ca. 75 % Kompressionsrate.
Empfohlene Parameter für verschiedene Traffic-Szenarien?
Niedriger Traffic (<1000 QPS): worker_connections 1024, keepalive 16; mittlerer Traffic (1000–5000 QPS): 2048, keepalive 32; hoher Traffic (>5000 QPS): 4096, keepalive 64. Anpassung anhand von Lasttest-Daten.

12 Min. Lesezeit · Veröffentlicht am: 15. Mai 2026 · Aktualisiert am: 14. Juli 2026

Lesepfad der SerieTeil 6 von 6

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.

Serienübersicht öffnen

Ähnliche Beiträge

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog