Nginx Reverse Proxy: upstream, Buffer und Timeouts im Überblick

Das Handy vibriert ununterbrochen – Alarm in der Produktion.
Im Log nur 502 Bad Gateway. Das Backend lief, aber die Timeout-Werte in der Nginx-Konfiguration waren zu knapp. Beim Traffic-Peak wurden Anfragen abgebrochen, bevor die Verarbeitung fertig war. Die Zeile proxy_read_timeout 60s war damals ein schneller Platzhalter.
Nach dem Vorfall eine Woche investiert, um die drei Kernmodule des Nginx Reverse Proxy wirklich zu verstehen: upstream-Lastverteilung, proxy buffer und Timeout-Konfiguration. Richtig eingestellt, hält der Reverse Proxy zehnfach mehr Last; falsch konfiguriert endet es wie damals um drei Uhr nachts.
Dieser Artikel bündelt Fehler, Debugging-Erfahrungen und die dahinterliegende Logik. Wer Backend oder DevOps macht – oder einfach verstehen will, was Nginx-Parameter bedeuten – spart damit Zeit.
Upstream-Lastverteilung: mehr als „Anfragen verteilen“
Grundsyntax
Der upstream-Block ist das Herz der Nginx-Lastverteilung. Die Basis kennen Sie vermutlich:
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
location / {
proxy_pass http://backend;
}
}
Einfach – Backend-Server definieren, proxy_pass zeigt dorthin. In Produktion reicht das nicht: Was passiert bei Ausfall? Wie gewichtet man stärkere Maschinen? Wie hält man Long-Living-Connections?
Vier Algorithmen, vier Szenarien
Standard ist Round-robin: der Reihe nach, fair, aber nicht intelligent.
Bei Long-Living-Connections – WebSocket, DB-Connection-Pools – kann Round-robin einzelne Server überlasten. Dann passt least_conn besser:
upstream backend {
least_conn;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
Nginx zählt aktive Verbindungen pro Server und leitet neue Anfragen an den am wenigsten belasteten. In einem WebSocket-Projekt explodierte bei Round-robin der Speicher auf einem Server – mit least_conn verteilte sich die Last deutlich gleichmäßiger.
Manchmal muss ein Nutzer nach dem Login immer denselben Server treffen (Session lokal gespeichert). Dafür gibt es ip_hash:
upstream backend {
ip_hash;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
Gleiche Client-IP landet immer auf demselben Backend. Schwachstelle: fällt der Server aus, ist die Session weg. Robuster: Session in Redis, ip_hash nur als Übergang.
Viertens: konsistentes Hashing für verteilte Caches:
upstream backend {
hash $request_uri consistent;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
Nginx legt 160 virtuelle Knoten pro Weight-Einheit an; dieselbe URI trifft immer denselben Server – hohe Cache-Trefferquote.
Gewichtung: unterschiedliche Serverleistung
Nicht jeder Server ist gleich: 32 GB RAM und 8 Kerne neben 16 GB und 4 Kernen. Faire Round-robin-Verteilung verschwendet Kapazität.
upstream backend {
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 weight=1;
}
weight=3 erhält dreimal so viele Anfragen. Stärkere Maschinen mehr Arbeit – schwächere weniger.
Mit backup reservieren Sie einen Ersatzserver:
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080 backup;
}
Der backup-Server nimmt erst Traffic an, wenn alle primären Server ausfallen – Ersatzbank für die Erstelf.
keepalive-Connection-Pool: der Performance-Hebel
Oft übersehen: Nginx öffnet standardmäßig pro Anfrage eine neue TCP-Verbindung zum Backend und schließt sie danach.
Drei-Wege-Handshake beim Aufbau, vier-Wege-Handshake beim Schließen – bei hoher Concurrency teuer. keepalive recycelt Verbindungen.
upstream backend {
server 192.168.1.10:8080;
keepalive 32; # pro Worker bis zu 32 idle Verbindungen
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
Zwei Punkte:
keepalive 32– maximale idle Verbindungen pro Worker-Prozessproxy_http_version 1.1undConnection ""sind Pflicht – HTTP/1.0 unterstützt keine persistenten Verbindungen
In einem API-Test: ohne keepalive etwa 2.000 QPS, danach 4.000+. Verdoppeln ist realistisch, kein Marketing.
Aber: keepalive nicht zu hoch setzen. Einmal 100 in der Testumgebung bei nur einem ECS-Container – das Backend war am Connection-Limit. Grobe Formel für Produktion:
keepalive ≈ Gesamt-QPS ÷ durchschnittliche Antwortzeit ÷ Worker-Prozesse
Beispiel: QPS 10.000, Antwortzeit 50 ms, 4 Worker:
10000 × 0,05 ÷ 4 = 125
keepalive um 125 ist ein guter Ausgangspunkt.
Health Checks: Ausfälle automatisch aussortieren
Die Open-Source-Version bietet nur passive Checks – ein Server gilt erst als ungesund, wenn Anfragen scheitern:
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
server {
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}
}
proxy_next_upstream definiert Retry-Bedingungen: Verbindungsfehler, Timeout oder 502/503/504. proxy_next_upstream_tries 3 – maximal drei Server versuchen.
Passiv heißt Verzögerung: Erst ein fehlgeschlagener Request markiert den Server. Bei hohen Verfügbarkeitsanforderungen lohnt aktives Checking in NGINX Plus:
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
server {
location / {
proxy_pass http://backend;
health_check interval=5s fails=3 passes=2;
}
}
Alle 5 Sekunden aktive Sondierung; drei Fehlschläge → unhealthy, zwei Erfolge → wieder healthy.
Proxy Buffer: Helfer oder Störfaktor?
Wozu dient Buffering?
Nginx speichert Backend-Antworten nicht sofort beim Client, sondern erst im Buffer.
Warum? Client-Bandbreite ist unvorhersehbar. Das Backend liefert schnell, der Client empfängt langsam – ohne Buffer blockiert Nginx und hält das Backend auf. Mit Buffer nimmt Nginx die Antwort entgegen und liefert sie in eigenem Tempo; das Backend kann die nächste Anfrage bearbeiten.
Kosten: Speicher. Große Response-Bodies bei hoher Concurrency verbrauchen viel RAM.
Drei Kernparameter und ihr Zusammenspiel
proxy_buffer_size 4k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
Am Anfang verwirrend – ähnliche Namen, unterschiedliche Rollen:
proxy_buffer_size: Buffer für Response-Header, einer pro Requestproxy_buffers: Array für den Response-Body – FormatAnzahl Größeproxy_busy_buffers_size: gerade an den Client gesendeter Anteil; maximal die Hälfte der Gesamt-buffers
Beispiel: proxy_buffers 8 32k → 8 × 32k = 256k gesamt. proxy_busy_buffers_size 64k ist ein Viertel – regelkonform.
Wann anpassen?
Sehr große Response-Header (viele Cookies) → Fehler „upstream sent too big header“. Lösung: proxy_buffer_size erhöhen:
proxy_buffer_size 16k;
Große Response-Bodies (lange JSON-Antworten):
proxy_buffers 16 64k;
Spezialfälle: Buffering abschalten
Manchmal stört Buffering.
Server-Sent Events (SSE): kontinuierlicher Event-Stream – mit Buffering kommen Nachrichten verzögert an:
location /events {
proxy_pass http://backend;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400s;
}
proxy_read_timeout 86400s (ein Tag), weil SSE Long-Living ist und nicht durch Timeout getrennt werden darf.
WebSocket – bidirektionale Echtzeitkommunikation:
location /ws {
proxy_pass http://backend;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
}
Große Uploads: 1 GB hochladen und erst komplett puffern, dann weiterleiten – Speicherexplosion. Request-Buffering aus:
location /upload {
proxy_pass http://backend;
proxy_request_buffering off;
client_max_body_size 1G;
}
proxy_request_buffering off streamt beim Empfang direkt weiter.
Timeouts: die Logik hinter den Parametern
Drei Timeouts, drei Aufgaben
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
Ähnliche Namen, klare Trennung:
proxy_connect_timeout: Wartezeit auf TCP-Verbindung zum Backend (Netzwerk, Firewall)proxy_read_timeout: nach Verbindungsaufbau – maximales Intervall zwischen zwei Leseoperationen vom Backendproxy_send_timeout: Zeitlimit zum Senden des Request-Bodies ans Backend
Häufige Verwechslung: proxy_read_timeout ist keine Gesamtzeit, sondern das Intervall zwischen Reads. Verarbeitet das Backend fünf Minuten, sendet aber Heartbeats, reichen 60s. Bleibt es fünf Minuten stumm, brauchen Sie proxy_read_timeout 300s.
Timeouts und 502/504
Die Lektion vom Alarm um drei Uhr nachts:
- 502 Bad Gateway: Nginx erreicht das Backend nicht – Dienst down, Port blockiert, Firewall
- 504 Gateway Timeout: Verbindung steht, Backend antwortet zu langsam
Beispiel: proxy_connect_timeout 10s, Backend antwortet erst nach 15s auf Connect → 502. Connect schnell, Verarbeitung zwei Minuten, proxy_read_timeout 60s → 504.
Timeout-Strategien je Szenario
API-Dienste: 30–60 Sekunden reichen meist. Kurze Timeouts decken langsame Requests schneller auf:
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
Dateiverarbeitung: Reports, PDF-Generierung – Minuten statt Sekunden:
proxy_connect_timeout 10s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
Streaming: Live-Video, WebSocket, SSE – Long-Living, ein Tag ist normal:
proxy_read_timeout 86400s;
502/504 in der Praxis debuggen
Typische Ursachen
Aus eigener Erfahrung:
- Backend wirklich down: Prozess abgestürzt, Port belegt, OOM
- Connection-Limit erreicht: Backend-Pool voll, Nginx kommt nicht durch
- Timeout zu kurz: wie beim Nachtalarm –
proxy_read_timeout 60s, Backend braucht zwei Minuten - Firewall/Netzwerk: Security Group, iptables blockieren
Log-Analyse
Immer zuerst error_log:
error_log /var/log/nginx/error.log warn;
Typische Meldungen:
upstream timed out (110: Connection timed out) while reading response header from upstream
→ 504, Lese-Timeout.
connect() failed (111: Connection refused) while connecting to upstream
→ 502, Verbindung abgelehnt – Backend lauscht nicht.
Fortgeschritten: eigenes Log-Format mit upstream-Status:
log_format upstream_status '$status $upstream_status $upstream_response_time';
access_log /var/log/nginx/access.log upstream_status;
Ausgabe wie 200 200, 200, 502 0.5, 1.2, 3.0 – Status pro Backend und Laufzeit auf einen Blick.
Typische Lösungen
Szenario 1: langsames Backend, häufig 504
proxy_read_timeout erhöhen – und prüfen, ob das Backend wirklich fertig wird. Nginx allein reicht nicht; Backend-Timeouts synchron anpassen.
Szenario 2: Connection refused, 502
Prozess läuft? Port offen? Firewall-Regeln?
netstat -tlnp | grep 8080
ps aux | grep your_app
Szenario 3: Connection-Limit unter Last
Backend-Pool vergrößern oder upstream keepalive aktivieren, um neue Verbindungen zu sparen.
Performance-Best-Practices
Worker-Konfiguration
Nginx ist multi-prozessfähig. worker_processes – üblicherweise Anzahl CPU-Kerne:
worker_processes auto;
auto erkennt Kerne automatisch. Acht Kerne → acht Worker.
worker_connections – maximale Verbindungen pro Worker:
events {
worker_connections 4096;
}
Theoretisches Maximum: worker_processes × worker_connections. 8 × 4096 = 32.768 – in der Praxis begrenzt durch Dateideskriptoren.
TCP-Optimierung: das Dreierpaket
sendfile on;
tcp_nopush on;
tcp_nodelay on;
Kombiniert spürbar schneller:
sendfile on: Kernel-Transfer, umgeht Userspace-Buffertcp_nopush on: mit sendfile Pakete bündeln statt einzeln sendentcp_nodelay on: kleine Pakete sofort, ohne auf Buffer-Füllung zu warten
Bei statischen Dateien messbar 30 %+ Durchsatzsteigerung.
Weitere Optimierungen
gzip-Kompression: Textantworten komprimieren, Bandbreite sparen:
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1024;
Dateideskriptor-Limit: unter hoher Last oft zu niedrig:
ulimit -n
Nur 1024? In /etc/security/limits.conf anheben:
* soft nofile 65535
* hard nofile 65535
Vollständiges Konfigurationsbeispiel
Empfohlene Produktionsvorlage:
# Basis
worker_processes auto;
events {
worker_connections 4096;
multi_accept on;
}
http {
# TCP
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Keepalive
keepalive_timeout 30;
keepalive_requests 100;
# Buffer
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
# Timeouts
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
# gzip
gzip on;
gzip_types text/plain text/css application/json;
upstream backend {
least_conn;
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 backup;
keepalive 32;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}
# SSE
location /events {
proxy_pass http://backend;
proxy_buffering off;
proxy_read_timeout 86400s;
}
}
}
Fazit
Kernpunkt in drei Sätzen:
- upstream: passenden Algorithmus wählen, keepalive aktivieren, Health Checks einplanen
- Buffer: drei Parameter verstehen; bei SSE/WebSocket/Uploads Buffering abschalten
- Timeouts: drei Parameter unterscheiden; Szenario bestimmt die Werte
Der Nachtalarm lehrte: Nginx-Konfiguration ist kein Parameter-Ausfüllen. Jeder Wert hat Design-Hintergrund – wer die Logik versteht, vermeidet die typischen Fallen.
Neu bei Nginx? Mit Defaults starten und gezielt nachjustieren – nicht wie damals proxy_read_timeout 60s blind in Produktion. Schon Erfahrung gesammelt? Dieser Artikel ordnet lose Erfahrungen zu einem System.
Beim Abschluss kurz in die Produktionskonfiguration geschaut: keepalive 32, proxy_read_timeout 120s, least_conn. Der Alarm um drei Uhr nachts ist nicht wiedergekommen.
FAQ
Ist proxy_read_timeout eine Gesamtzeit oder ein Intervall zwischen Leseoperationen?
Wann sollte man proxy_buffering abschalten?
• Server-Sent Events (SSE): Echtzeit-Push – Buffering verzögert Nachrichten
• WebSocket: bidirektionale Echtzeitkommunikation, Streaming erforderlich
• große Uploads: Speicher sparen, Daten beim Empfang direkt weiterleiten
Welcher keepalive-Wert ist sinnvoll?
Was ist der Unterschied zwischen 502 und 504?
Welchen Lastverteilungsalgorithmus wählen?
• Round-robin (Default): zustandslose Dienste, faire Verteilung
• least_conn: Long-Living-Connections (WebSocket, DB-Connection-Pool)
• ip_hash: Session-Affinität (Übergangslösung – Redis ist robuster)
• hash: verteilter Cache, höhere Trefferquote
Wie behebt man „upstream sent too big header“?
Warum verbessern sendfile + tcp_nopush + tcp_nodelay die Performance?
8 Min. Lesezeit · Veröffentlicht am: 30. März 2026 · Aktualisiert am: 14. Juli 2026
Nginx Praxisleitfaden
Du liest den ersten Beitrag dieser Serie. Lies den nächsten Beitrag oder öffne die Serienübersicht, um den gesamten Pfad zu sehen.
Vorheriger
Du bist am Anfang dieser Serie.
Nächster
Nginx Performance-Tuning: gzip, Cache und Connection-Pool-Konfiguration
Kernkonfigurationen für Nginx Performance-Tuning: gzip-Komprimierung reduziert 60–80 % des Übertragungsvolumens, proxy_cache-Strategien erreichen 95 % Hit-Rate, worker_connections steigert die Parallelität um das 3–4-Fache
Teil 2 von 6
Ähnliche Beiträge
Nginx SSL/TLS in der Praxis: von HTTPS-Zertifikaten bis A+ Sicherheitshärtung

Nginx SSL/TLS in der Praxis: von HTTPS-Zertifikaten bis A+ Sicherheitshärtung
Nginx Lastverteilung in der Praxis: upstream-Konfiguration und Health Checks


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