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

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog