Behebung von Nginx 504 Gateway Timeout und Client-Timeout-Problemen

Meistern Sie Nginx-Timeouts, einschließlich des gefürchteten 504 Gateway Timeout, indem Sie lernen, kritische Proxy-Direktiven anzupassen. Diese Anleitung erklärt detailliert, wie Sie `proxy_read_timeout` erhöhen, das Buffering optimieren und Fehlerprotokolle zur Diagnose von Kommunikationsfehlern zwischen Nginx und Upstream-Servern für eine robuste Verbindungsverwaltung nutzen.

Behebung von Nginx 504 Gateway Timeout und Client-Timeout-Problemen

Ein Nginx 504 Gateway Timeout bedeutet, dass Nginx als Proxy oder Gateway fungierte und innerhalb der vorgegebenen Zeit keine Antwort vom Upstream-Dienst erhalten hat. Der Upstream könnte eine Node.js-App, Gunicorn, PHP-FPM, ein anderer Nginx-Dienst, eine interne API oder ein Load Balancer sein.

Die verlockende Lösung ist, jedes Timeout auf fünf Minuten zu erhöhen und weiterzumachen. Manchmal ist ein längeres Timeout kurzfristig die richtige Maßnahme, insbesondere für Berichte, Importe oder Admin-Jobs. Aber wenn eine normale Benutzeranfrage länger braucht, als Nginx derzeit erlaubt, sollten Sie auch fragen, warum das Backend langsam ist, ob die Anfrage asynchron sein sollte und ob ein anderer Proxy im Pfad ohnehin ein kürzeres Timeout hat.


Den 504 Gateway Timeout-Fehler verstehen

Ein 504 Gateway Timeout-Fehler tritt auf, wenn Nginx, das als Reverse-Proxy oder Gateway fungiert, keine rechtzeitige Antwort vom Upstream-Server erhält, an den es Anfragen weiterleitet. Einfach ausgedrückt: Nginx hat das Backend um eine Antwort gebeten, die konfigurierte Zeit gewartet und aufgegeben, weil keine Antwort eingetroffen ist.

Dies unterscheidet sich von einem 502 Bad Gateway, bei dem Nginx eine ungültige oder vorzeitig geschlossene Upstream-Antwort erhalten hat, und von einem 503 Service Unavailable, was oft bedeutet, dass ein Dienst absichtlich nicht verfügbar oder überlastet ist. Die Unterscheidung ist wichtig, da ein 504 auf Wartezeit, Timeout und Upstream-Latenz hinweist.

Wichtige Direktiven zur Steuerung von Upstream-Timeouts

Beim Proxying von Anfragen verwendet Nginx mehrere kritische Direktiven, die sich hauptsächlich in den Blöcken http, server oder location oder speziell in einem upstream-Block befinden. Die Anpassung dieser Werte ist die primäre Methode zur Lösung von 504-Fehlern.

1. proxy_connect_timeout

Legt das Timeout für den Aufbau einer Verbindung zum Upstream-Server fest. Wenn Nginx innerhalb dieser Zeitspanne keine Verbindung herstellen kann, wird ein Timeout-Fehler zurückgegeben.

Standard: 60 Sekunden

proxy_connect_timeout 60s;

2. proxy_send_timeout

Legt das Timeout für die Zeit zwischen zwei aufeinanderfolgenden Schreibvorgängen an den Upstream-Server fest. Dies ist relevant beim Senden eines großen Anforderungstextes.

Standard: 60 Sekunden

proxy_send_timeout 60s;

3. proxy_read_timeout (Die häufigste Lösung für 504er)

Legt das Timeout für das Warten auf eine Antwort vom Upstream-Server fest, nachdem die Anforderungsheader gesendet wurden. Wenn die Backend-Anwendung zu lange braucht, um die Anfrage zu verarbeiten und einen Antworttext zu generieren, ist dies die Direktive, die erhöht werden muss.

Standard: 60 Sekunden

# Beispiel: Erhöhung des Read-Timeout auf 120 Sekunden für eine langsame API
proxy_read_timeout 120s;

Wenn Ihre Anwendung häufig den Standardwert überschreitet, erhöhen Sie diesen Wert vorsichtig und untersuchen Sie weiter. Ein sehr hoher Wert kann Client-Verbindungen offen halten, während das Backend bereits fehlerhaft ist.


Behebung von Client-Seitigen Timeouts

Client-seitige Timeouts sind ein anderer Fehler. Der Browser, die mobile App, der Load Balancer, das CDN oder der aufrufende Dienst gibt auf, bevor Nginx die Antwort beendet hat. In diesem Fall sieht der Benutzer möglicherweise einen Browserfehler oder einen Gateway-Fehler von einer Schicht vor Nginx, während Nginx möglicherweise eine geschlossene Verbindung protokolliert, anstatt einen sauberen 504.

Wenn Sie Client-Timeout bevor Nginx einen 504 protokolliert, müssen Sie die Verbindung zwischen dem Client und Nginx untersuchen.

1. Client-Seitiges Keepalive

Wenn der Client die Verbindung vorzeitig schließt, könnte Nginx einen Fehler erhalten oder der Client könnte einfach beim Warten auf Daten ein Timeout haben.

Wenn der Client ein anderer Proxy oder Load Balancer ist, überprüfen Sie dessen Timeout-Einstellungen im Vergleich zu Nginx und dem Backend. Das kürzeste Timeout in der Kette gewinnt normalerweise. Ein häufiges Muster ist: CDN wartet 100 Sekunden, Load Balancer wartet 60 Sekunden, Nginx wartet 180 Sekunden, Backend braucht 120 Sekunden. Benutzer scheitern trotzdem nach 60 Sekunden, weil der Load Balancer zuerst aufgibt.

2. Nginx send_timeout

Diese Direktive steuert, wie lange Nginx darauf wartet, dass der Client Daten bestätigt oder empfängt (die Zeit zwischen zwei aufeinanderfolgenden Schreibvorgängen an den Client).

Standard: 60 Sekunden

# Setzen Sie dies, wenn Clients ein Timeout haben, während Nginx die Antwort sendet
send_timeout 120s;

Optimierung des Buffering für große Antworten

Manchmal beginnt das Backend zu antworten, aber die Zustellung ist trotzdem langsam, weil die Antwort riesig ist, der Client langsam ist oder Nginx mehr puffern muss als erwartet. Dies ist häufig bei generierten CSV-Exporten, Mediendownloads, die über eine App geleitet werden, oder APIs, die sehr große JSON-Payloads zurückgeben.

Nginx verwendet Puffer, um Daten, die vom Upstream empfangen wurden, vorübergehend zu speichern, bevor sie an den Client gesendet werden. Wenn die Antwort sehr groß ist, können diese Puffer überschritten werden, was zu komplexer Handhabung oder wahrgenommener Latenz führt.

Wichtige Buffering-Direktiven

Diese werden normalerweise im location-Block oder server-Block gesetzt:

Direktive Zweck
proxy_buffers Legt die Anzahl und Größe der Puffer fest, die zum Lesen der Antwort vom Upstream verwendet werden. Format: Anzahl Größe;
proxy_buffer_size Legt die Größe des ersten Puffers fest, der zum Lesen des Antwort-Headers verwendet wird.
proxy_max_temp_file_size Wenn die Antwort die verfügbaren Puffer überschreitet, schreibt Nginx in temporäre Dateien. Dies legt die maximale Größe für diese temporären Dateien fest.

Beispielkonfiguration für hohes Volumen/große Antworten:

location /api/heavy_report {
    proxy_pass http://backend_app;

    # Erhöhen Sie das Read-Timeout
    proxy_read_timeout 180s;

    # Buffering für potenziell große Antworttexte optimieren
    # Verwenden Sie 8 Puffer, jeder bis zu 1 MB (1024k)
    proxy_buffers 8 1024k;
    proxy_buffer_size 256k;

    # Temporäre Dateien bis zu 500 MB zulassen, wenn Puffer überlaufen
    proxy_max_temp_file_size 500m;
}

Wenn Ihre Backend-Antwort wirklich riesig ist, sollten Sie erwägen, eine generierte Datei aus einem Objektspeicher oder statischen Speicher bereitzustellen, anstatt die Anfrage über die App offen zu halten. Für Exporte ist ein häufiges Muster: Job in die Warteschlange stellen, Datei generieren, dann den Benutzer die Datei von einer statischen URL herunterladen lassen, wenn sie bereit ist.


Fehlerbehebungsschritte und Log-Analyse

Die Behebung von Timeouts erfordert die genaue Lokalisierung des Staus: Client -> Nginx oder Nginx -> Backend.

Schritt 1: Überprüfen Sie die Nginx-Fehlerprotokolle

Das Nginx-Fehlerprotokoll ist Ihre definitive Quelle, um festzustellen, ob Nginx beim Warten auf das Backend ein Timeout hatte.

Suchen Sie nach Einträgen mit Phrasen wie:

  • upstream timed out (110: Connection timed out)
  • upstream prematurely closed connection while reading response header from upstream

Wenn Sie diese sehen, liegt das Problem am proxy_read_timeout oder an der Verarbeitungszeit des Backends.

Suchen Sie auch nach client prematurely closed connection. Das bedeutet normalerweise, dass der Client oder ein Proxy vor Nginx zuerst aufgegeben hat. In diesem Fall hilft eine Erhöhung von proxy_read_timeout allein dem Benutzer nicht.

Schritt 2: Überprüfen Sie die Backend-Anwendungsprotokolle

Wenn Nginx ein Timeout hat (Protokolle zeigen 504 an), überprüfen Sie sofort die Protokolle des Upstream-Dienstes (z. B. PHP-FPM-Protokolle, Gunicorn-Protokolle, Java-Anwendungsserver-Protokolle). Sie müssen bestätigen, ob die Anfrage überhaupt das Backend erreicht hat und wie lange sie zur Bearbeitung gebraucht hat.

  • Wenn die Backend-Protokolle zeigen, dass die Anfrage länger gedauert hat als Ihr konfiguriertes proxy_read_timeout, erhöhen Sie das Nginx-Timeout.
  • Wenn die Backend-Protokolle zeigen, dass die Anfrage schnell abgeschlossen wurde, könnte das Problem eine Netzwerklatenz zwischen Nginx und dem Backend oder ein falsch konfiguriertes Client-Timeout gegenüber Nginx sein.

Schritt 3: Verwenden Sie den Header X-Upstream-Response-Time (Optional)

Für detaillierte Diagnosen können Sie die genaue Zeit, die der Upstream für die Antwort benötigt hat, mit der Variable $upstream_response_time in Ihrem Zugriffsprotokollformat protokollieren. Dies hilft, die tatsächliche Leistung des Backends zu bestätigen.

In Ihrer nginx.conf:

log_format proxy_detailed '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" $request_time $upstream_response_time';

access_log /var/log/nginx/access.log proxy_detailed;

Durch die Analyse von $upstream_response_time können Sie die genaue Dauer sehen, die Nginx gewartet hat, unabhängig von Nginx eigenen Timeout-Einstellungen.

Für einen schnellen einmaligen Test rufen Sie den Upstream direkt vom Nginx-Host aus auf:

time curl -sS -o /dev/null -w 'status=%{http_code} total=%{time_total}\n' http://127.0.0.1:3000/slow-route

Wenn der direkte Upstream-Aufruf bereits langsam ist, meldet Nginx nur das Problem. Wenn der direkte Aufruf schnell ist, aber die proxierte Anfrage ein Timeout hat, überprüfen Sie die Proxy-Konfiguration, DNS-Auflösung, Container-Netzwerke, TLS zwischen internen Diensten oder einen anderen Hop zwischen Nginx und der App.


Wenden Sie die kleinste sinnvolle Änderung an

Eine vernünftige Produktionslösung sieht oft so aus:

location /api/reports/ {
    proxy_pass http://backend_app;
    proxy_connect_timeout 10s;
    proxy_send_timeout 60s;
    proxy_read_timeout 180s;
}

Das erhöht das Read-Timeout nur für den langsamen Berichts-Endpunkt. Es lässt nicht jede Anfrage auf der Site drei Minuten warten. Für eine Login-Route, eine Checkout-Route, einen Health Check oder einen öffentlichen API-Endpunkt kann ein langes Timeout Fehler schmerzhafter machen, weil Clients länger auf eine Anfrage warten, die sich wahrscheinlich nicht erholt.

Für PHP-FPM können die entsprechenden FastCGI-Direktiven sein:

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    fastcgi_read_timeout 120s;
    include fastcgi_params;
}

Denken Sie daran, dass PHP, Python, Node.js, Anwendungsserver, Warteschlangen, Datenbanken, CDNs und Load Balancer alle ihre eigenen Timeout-Einstellungen haben können. Nginx kann ein Backend nicht dazu bringen, weiterzuarbeiten, nachdem das eigene Worker-Timeout des Backends die Anfrage beendet hat.

Nachdem Sie Konfigurationsänderungen vorgenommen haben (z. B. Erhöhung von Timeouts oder Anpassung von Puffergrößen), testen Sie immer die Konfigurationssyntax und laden Sie Nginx neu:

sudo nginx -t
sudo systemctl reload nginx

Beobachten Sie dann sowohl die Nginx- als auch die Upstream-Protokolle, während Sie dieselbe Anfrage wiederholen:

sudo tail -f /var/log/nginx/error.log

Die beste Timeout-Lösung hinterlässt einen klaren Grund für die Änderung: Diese Route benötigt legitimerweise bis zu zwei Minuten, dieser Upstream hat jetzt passende Grenzen, und langsame Anfragen sind in Protokollen oder Metriken sichtbar. Alles andere ist ein temporärer Patch, und temporäre Patches sollten in Ihrer Konfigurationsüberprüfung oder Ihren Incident-Notizen entsprechend gekennzeichnet werden.