Fehlerbehebung

502 Bad Gateway: was der Fehler 502 bedeutet und wie du ihn behebst

Was heißt 502 Bad Gateway? Ein Proxy hat vom Server dahinter eine ungültige oder gar keine Antwort bekommen. Wer den Fehler 502 zeigt und wie du ihn behebst.

Veröffentlicht ~10 Min. Lesezeit
Auf dieser Seite

Kurz gesagt: Der Fehler 502 Bad Gateway heißt, dass ein Gateway oder Proxy (nginx, Apache, ein Load Balancer oder ein CDN) vom Server dahinter eine ungültige Antwort bekommen hat, oder gar keine. Wörtlich übersetzt bedeutet Bad Gateway „fehlerhaftes Gateway“. Der Proxy funktioniert; die Anwendung dahinter nicht. Besucher können neu laden und warten. Seitenbetreiber finden die Ursache im Error-Log des Proxys.

Debian · Ubuntu · auf dem Server, direkt nach einer 502
# 1. Was hat der Proxy gesehen?
sudo tail -n 20 /var/log/nginx/error.log          # Apache: /var/log/apache2/error.log

# 2. Läuft das Backend, und lauscht es dort, wo der Proxy es erwartet?
sudo systemctl status php8.3-fpm --no-pager       # oder der Dienst deiner App
sudo ss -tlnp | grep ':3000'                      # TCP-Backends
sudo ss -xlp | grep -E 'php|gunicorn|sock'        # Unix-Sockets

# 3. Das Backend direkt fragen, ohne Proxy
curl -sS -o /dev/null -w '%{http_code} in %{time_total}s\n' http://127.0.0.1:3000/

Ersetze php8.3-fpm durch die PHP-Version, die du nutzt (ls /run/php/ zeigt sie), und 3000 durch den Port aus deinem proxy_pass. Antwortet das Backend direkt, aber nicht über den Proxy, liegt das Problem bei Adresse, Socket oder Rechten zwischen den beiden.

Was heißt 502 Bad Gateway?

RFC 9110 beschreibt 502 sinngemäß so: Ein Server hat „als Gateway oder Proxy beim Versuch, die Anfrage zu erfüllen, eine ungültige Antwort von einem dahinterliegenden Server erhalten, auf den er zugegriffen hat“. In der Praxis nutzen die meisten Proxys den Code auch, wenn gar keine Antwort kam: Die Verbindung wurde abgelehnt, der Socket existierte nicht, oder das Backend hat die Verbindung geschlossen, bevor es antwortete.

Entscheidend ist, wer ihn sendet. Eine 502 kommt vom vorgeschalteten Proxy und meldet ein Problem mit dem Server dahinter. Stürzt deine Anwendung ab, sendet sie keine 502; sie antwortet einfach nicht mehr, und nginx oder der Load Balancer meldet das. (Eine App, die ihrerseits einen anderen Dienst aufruft, kann dessen 502 weiterreichen, dann steht das in ihrem eigenen Log.) Browser zeigen die Seite des Proxys, deshalb erscheint derselbe Fehler als 502 Bad Gateway, HTTP Error 502, 502 Proxy Error oder als eigens gestaltete Fehlerseite, je nachdem, was davor sitzt.

Wer zeigt die 502?

Was du siehstGesendet vonWas es meist bedeutet
Weiße Seite, 502 Bad Gateway, Fußzeile nginxnginxBackend aus, falscher Socket oder Port, oder es hat die Verbindung geschlossen
Proxy Error. The proxy server received an invalid response from an upstream server.Apache mod_proxyDie Antwort des Backends brach ab oder war kein HTTP (AH01102)
502 Bad Gateway. The server returned an invalid or incomplete response.HAProxyDer Server im Backend hat nichts Brauchbares gesendet
Leere Seite mit Status 502CaddySein Log nennt den Verbindungsfehler, etwa connect: connection refused
Bad GatewayTraefikKeine Antwort vom Dienst oder Container
HTTP Error 502.3 oder 502.5 Process FailureIIS (ARR oder das ASP.NET-Core-Modul)Das Backend ist gescheitert oder hat zu lange gebraucht, oder die App ist nicht gestartet
502-Seite im Cloudflare-DesignDas Netzwerk von Cloudflare selbstEin Problem innerhalb von Cloudflare; prüfe die Statusseite
Schlichte 502-Seite, während die Website hinter Cloudflare läuftDer Proxy deines Origin-Servers, durchgereichtAlles auf dieser Seite gilt

Apache und HAProxy verhalten sich hier anders als nginx: Erreichen sie das Backend gar nicht, antworten sie (Apache mit AH01114 im Log) mit 503 Service Unavailable (engl.) und heben sich 502 für ein Backend auf, das etwas Kaputtes geantwortet hat. nginx und Caddy nutzen 502 für beides.

Cloudflare: die eigene 502 oder einer der Codes 520 bis 526

Bekommt Cloudflare selbst keine brauchbare Antwort von deinem Origin-Server, nutzt es eigene Codes statt 502. Laut der Dokumentation von Cloudflare weist eine 502 oder 504 im Cloudflare-Design auf das eigene Netzwerk hin, während eine schlichte Seite ohne Branding von deinem Server kam und nur durchgereicht wurde. Zu jedem Origin-Code gibt es eine eigene Anleitung:

CodeWas Cloudflare gesehen hat
520 (engl.)Der Origin hat leer, mit einem Reset oder fehlerhaft geantwortet
521 (engl.)Der Origin hat die Verbindung abgelehnt: Webserver gestoppt oder Port geschlossen
522 (engl.)Die TCP-Verbindung zum Origin lief in ein Timeout
523 (engl.)Der Origin ist nicht erreichbar: falsche IP, Routing oder DNS
524 (engl.)Verbunden, aber keine HTTP-Antwort in der vorgesehenen Zeit
525 (engl.)Der TLS-Handshake mit dem Origin ist fehlgeschlagen
526 (engl.)Das Zertifikat des Origins ist im Modus Full (strict) ungültig

Wie behebe ich einen Fehler 502 als Besucher?

  • Nach einer Minute neu laden. Viele 502 dauern nur, solange eine App neu startet oder ein Deployment umschaltet. Jede Sekunde neu zu laden erhöht die Last und hilft nicht.
  • Andere Websites prüfen. Zeigt jede Website 502, liegt das Gateway auf deiner Seite: ein Firmen-Proxy, ein VPN oder eine Sicherheitssoftware, die den Datenverkehr prüft. Versuche es ohne.
  • Cookies und Cache kannst du lassen. Eine 502 entsteht zwischen zwei Servern, sie zu löschen ändert also selten etwas. Die Ausnahme ist eine Website, die gerade den Server gewechselt hat: Dort hilft es, den DNS-Cache zu leeren (engl.), damit du bei der neuen Adresse landest.
  • Dem Betreiber Bescheid sagen, wenn es bleibt. Notiere Uhrzeit und URL. Das reicht ihm, um die passende Zeile in seinem Log zu finden.

Für Seitenbetreiber: was im Error-Log steht

Der Proxy schreibt pro fehlgeschlagener Anfrage eine Zeile, und ihr Wortlaut nennt die Ursache. Das hier sind die Meldungen von nginx, denen du auf einem VPS am häufigsten begegnest:

Log-MeldungUrsacheLösung
connect() failed (111: Connection refused) while connecting to upstreamAuf dieser Adresse und diesem Port lauscht nichts: Die App ist aus, abgestürzt oder lauscht woandersStarten, Port prüfen
connect() to unix:/run/php/php8.3-fpm.sock failed (2: No such file or directory)Der Socket-Pfad ist falsch, oft nach einem PHP-Upgrade, oder FPM ist gestopptPfad korrigieren
(13: Permission denied) while connecting to upstreamnginx darf den Socket nicht öffnen, oder SELinux blockiert die VerbindungSocket-Besitzer oder SELinux-Boolean
upstream prematurely closed connection while reading response header from upstreamDas Backend ist abgestürzt oder hat die Anfrage beendet, bevor es antworteteAbsturz, OOM-Kill oder Worker-Timeout
recv() failed (104: Connection reset by peer) while reading response header from upstreamDieselbe Familie: Die Verbindung wurde mitten in der Anfrage zurückgesetztLog des Backends prüfen
upstream sent too big header while reading response header from upstreamDie Response-Header (meist Cookies) sind größer als der Puffer des ProxysPuffergröße erhöhen
no live upstreams while connecting to upstreamJeder Server im upstream-Block wurde als ausgefallen markiertBackends reparieren; nginx versucht sie nach fail_timeout erneut

Bei PHP-FPM lautet der Fall der geschlossenen Verbindung upstream prematurely closed FastCGI stdout. Das Gegenstück bei Apache ist AH01102: error reading status line from remote server. Die Anleitung zu nginx 502 Bad Gateway (engl.) hält die reine nginx-Checkliste kurz; die Ursachen unten gehen jeden Fall für jeden Proxy durch.

Den Upstream direkt testen

Wer den Proxy umgeht, teilt das Problem in zwei Hälften. Frag das Backend auf dem Server selbst, mit genau der Adresse aus deiner Konfiguration:

auf dem Server · HTTP-Backends
# TCP: Node, gunicorn, uvicorn, ein anderer Webserver
curl -sS -i http://127.0.0.1:3000/ | head -n 5

# Unix-Socket, zum Beispiel gunicorn auf /run/gunicorn.sock
curl -sS -i --unix-socket /run/gunicorn.sock http://localhost/ | head -n 5

Existiert die Socket-Datei nicht, gibt curl 7.88.1 unter Debian 12 eine irreführende Zeile aus: Sie nennt Port 80, obwohl curl das Netzwerk nie benutzt hat.

Ausgabe, Socket fehlt
curl: (7) Failed to connect to localhost port 80 after 0 ms: Couldn't connect to server

PHP-FPM spricht FastCGI, nicht HTTP, curl kann es also nicht testen. cgi-fcgi aus dem Paket libfcgi-bin kann es, über die Ping-Seite von FPM. Aktiviere sie im Pool mit ping.path = /ping, starte FPM neu, dann:

Debian · Ubuntu · Health Check für PHP-FPM
sudo apt install -y libfcgi-bin
sudo env SCRIPT_NAME=/ping SCRIPT_FILENAME=/ping REQUEST_METHOD=GET \
  cgi-fcgi -bind -connect /run/php/php8.3-fpm.sock
# ein paar Header-Zeilen, dann: pong

Kommt hier eine Antwort und über nginx eine 502, ist das Backend in Ordnung und der Weg zwischen beiden nicht. Kommt hier keine Antwort, beschwert sich der Proxy zu Recht.

Ursache 1: Das Backend läuft nicht

Connection refused heißt, auf dieser Adresse lauscht nichts. Sieh dir den Dienst und sein eigenes Log an, das journalctl (engl.) liest:

Debian · Ubuntu · Zustand des Dienstes und seine letzten Meldungen
sudo systemctl status myapp --no-pager
sudo journalctl -u myapp -n 50 --no-pager
sudo journalctl -u myapp --since "15 min ago" -p err
systemctl show myapp -p NRestarts         # bisherige automatische Neustarts

Active: activating (auto-restart) mit status=1/FAILURE ist eine Crash-Schleife: systemd startet die App neu, sie stürzt beim Start ab, und jede Anfrage dazwischen bekommt eine 502. Das Journal nennt den Grund, typischerweise eine fehlende Umgebungsvariable, einen Syntaxfehler nach einem Deployment, eine Datenbank, die sie nicht erreicht, oder einen schon belegten Port. Für den letzten Fall gibt es die Anleitung zu address already in use (engl.). Behebe die Ursache, dann sudo systemctl restart myapp.

Ursache 2: Der Proxy zeigt auf die falsche Adresse oder den falschen Socket

  • Ein PHP-Upgrade hat den Socket verschoben. Aus php8.2-fpm.sock wird php8.3-fpm.sock, und fastcgi_pass nennt noch den alten. ls /run/php/ zeigt, was existiert.
  • localhost ist nicht eine einzige Adresse. proxy_pass http://localhost:3000 löst sowohl zu ::1 als auch zu 127.0.0.1 auf, und nginx probiert die andere, wenn die erste ablehnt. Das Log füllt sich also mit abgelehnten Versuchen auf der Adresse, die die App nicht nutzt. Eine echte 502 folgt, wenn beide Seiten verschiedene Adressen nennen: nginx proxyt zu 127.0.0.1, während eine Node-App (ab Version 17), die an localhost gebunden ist, nur auf ::1 lauscht. Das Feld upstream: in der Log-Zeile zeigt, welche Adresse fehlschlug. Nutze auf beiden Seiten dieselbe ausgeschriebene Adresse.
  • Ein Container hat eine neue IP bekommen. Wer auf die interne IP eines Containers proxyt, läuft nach dessen Neustart ins Leere. Veröffentliche den Port auf 127.0.0.1 und proxye dorthin, oder setz den Proxy ins selbe Docker-Netzwerk und nutze den Dienstnamen.
vergleichen, was der Proxy will und was lauscht
sudo nginx -T 2>/dev/null | grep -nE 'proxy_pass|fastcgi_pass|server .*:'
sudo ss -tlnp
ls -l /run/php/

Ursache 3: Socket-Rechte und SELinux

(13: Permission denied) an einem Unix-Socket heißt, der Worker-Benutzer von nginx darf ihn nicht öffnen. Der PHP-FPM-Pool von Debian legt den Socket für www-data mit Modus 0660 an, was zum nginx von Debian passt. Die Pakete von nginx.org lassen nginx dagegen als Benutzer nginx laufen, und dann bleibt der Socket für ihn zu. Gleiche den Pool an den Benutzer an, unter dem nginx läuft:

/etc/php/8.3/fpm/pool.d/www.conf
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = nginx        ; die Gruppe des nginx-Worker-Benutzers
listen.mode = 0660

Starte FPM danach neu. Unter Rocky, AlmaLinux und RHEL steckt hinter demselben Fehler bei einem TCP-Backend wie 127.0.0.1:3000 meist SELinux, das dem Webserver Netzwerkverbindungen verbietet, bis du sie mit sudo setsebool -P httpd_can_network_connect 1 erlaubst. sudo ausearch -m avc -ts recent bestätigt die Ablehnung vorher.

Ursache 4: Das Backend ist mitten in der Anfrage abgestürzt oder wurde beendet

prematurely closed und Connection reset by peer heißen, das Backend hat die Anfrage angenommen und ist dann verschwunden. Drei Dinge führen dazu:

  • Der OOM-Killer des Kernels. sudo journalctl -k | grep -i 'killed process' zeigt ihn. PHP-FPM protokolliert den Verlust eines Workers als child 12345 exited on signal 9 (SIGKILL). Was den Speicher belegt hat, findest du mit der Anleitung zu vom OOM-Killer beendeten Prozessen (engl.).
  • Ein Absturz. exited on signal 11 (SIGSEGV) im FPM-Log deutet auf eine PHP-Erweiterung; eine unbehandelte Exception in Node beendet den Prozess, wenn nichts sie abfängt, und systemd startet ihn einen Moment später neu.
  • Das eigene Timeout des Backends. gunicorn beendet einen Worker standardmäßig nach 30 Sekunden, und request_terminate_timeout von PHP-FPM protokolliert execution timed out ... terminating. nginx sieht eine geschlossene Verbindung und antwortet mit 502 statt 504. Eigentlich ist das eine langsame Anfrage, und die behandelt die Anleitung zu nginx 504 Gateway Timeout (engl.).

Ursache 5: Die Response-Header sind zu groß

nginx liest die Response-Header des Backends in einen einzigen Puffer, standardmäßig 4 oder 8 KB (eine Speicherseite). Ein Login, der mehrere große Cookies setzt, oder ein Framework, das einen langen Content-Security-Policy- oder Link-Header schickt, sprengt ihn, und das Log meldet upstream sent too big header. Erhöhe proxy_buffer_size und proxy_buffers gemeinsam, sonst scheitert nginx -t mit einem Fehler zu proxy_busy_buffers_size:

nginx · server- oder location-Block
# proxy_pass-Backends
proxy_buffer_size 16k;
proxy_buffers 8 16k;

# PHP-FPM (fastcgi_pass)
fastcgi_buffer_size 16k;
fastcgi_buffers 8 16k;

Dann sudo nginx -t && sudo systemctl reload nginx. Wachsen die Header weiter, finde heraus, warum: Ein Cookie, das mit jedem Besuch größer wird, ist ein Bug, und Cloudflare lehnt Response-Header über seinem eigenen Limit mit einer 520 ab.

Ursache 6: eine Firewall oder Keep-Alive zwischen Proxy und Backend

  • Ein Backend auf einem anderen Server. Eine Firewall, die die Verbindung ablehnt, ergibt (111: Connection refused) oder (113: No route to host) und eine 502; eine, die Pakete verwirft, ergibt ein Timeout und eine 504. Erlaube die Adresse des Proxys auf dem Port des Backends, zum Beispiel mit ufw, und teste vom Proxy aus mit curl -v --connect-timeout 5 http://10.0.0.5:8080/.
  • Seltene, zufällige 502 hinter einem Load Balancer. Schließt das Backend ungenutzte Keep-Alive-Verbindungen früher, als der Balancer aufhört, sie wiederzuverwenden, landet gelegentlich eine Anfrage auf einer Verbindung, die sich gerade schließt. server.keepAliveTimeout von Node steht standardmäßig auf 5 Sekunden, kürzer als das Idle-Timeout der meisten Balancer. Stelle das Keep-Alive des Backends länger ein als das des Balancers.

502 vs. 503 vs. 504

Frage502 Bad Gateway503 Service Unavailable504 Gateway Timeout
Wer sendet den CodeEin Proxy oder GatewayDer Server selbst oder ein ProxyEin Proxy oder Gateway
Was passiert istDie Antwort des Backends war ungültig, abgebrochen oder nicht zu bekommenDer Server lehnt vorerst ab: Überlast, Wartung, ein Rate LimitDas Backend hat die Anfrage angenommen, aber nicht rechtzeitig geantwortet
Typisches nginx-Logconnect() failed (111)limiting requestsupstream timed out (110)
Retry-After-HeaderNeinOft, bei geplanter WartungNein
Erste PrüfungLäuft das Backend, und lauscht es dort, wo der Proxy es erwartet?Worker-Pool, Wartungsschalter, Rate LimitsLangsame Abfragen, langsame externe Aufrufe, Timeouts

Für 503 Service Unavailable (engl.) und 504 Gateway Timeout (engl.) gibt es eigene Anleitungen. Der andere Nachbar ist ein 500 Internal Server Error (engl.): Dort hat das Backend sehr wohl geantwortet, nur mit einem eigenen Fehler.

Antwortet das Backend wieder, ruft der HTTP-Header-Checker (engl.) die Seite von außerhalb deines Netzwerks ab und zeigt den Statuscode und die Header, die Besucher von außen bekommen.

502-Fehler auf dem eigenen Server vermeiden

  • systemd eine abgestürzte App neu starten lassen. Restart=on-failure in der Unit macht aus einem Absturz ein paar Sekunden 502 statt eines Ausfalls; die Unit dazu zeigt die Anleitung zum systemd-Dienst (engl.).
  • nginx neu laden statt neu starten. systemctl reload nginx startet neue Worker und lässt die alten ihre Anfragen zu Ende bringen; ein Restart bricht sie ab.
  • Speicherreserve lassen. Bemiss pm.max_children und die App-Worker nach dem RAM, den du hast, und ergänze Swap (engl.) als Puffer, damit eine Stoßzeit nicht mit dem OOM-Killer endet. Ein Standard-KVM-Tarif mit mehr Arbeitsspeicher ist die Lösung, wenn der Server für die Last schlicht zu klein geworden ist.
  • Von außen überwachen. Ein Monitor wie Uptime Kuma (engl.) bemerkt eine 502, bevor deine Besucher dir schreiben.

Häufige Fragen

Was heißt 502 Bad Gateway?

Ein Server, der als Gateway oder Proxy arbeitet, etwa nginx, Apache, ein Load Balancer oder ein CDN, hat vom Server dahinter eine ungültige Antwort bekommen oder gar keine. Der Proxy selbst funktioniert; die Anwendung oder der Server, an den er weiterleitet, ist ausgefallen, nicht erreichbar oder hat seine Antwort abgebrochen.

Ist ein Fehler 502 meine Schuld als Besucher?

Fast nie. Er entsteht zwischen zwei Servern, Cookies oder den Browser-Cache zu löschen hilft daher selten. Warte eine Minute und lade die Seite einmal neu. Zeigt jede Website 502, ist ein VPN, ein Firmen-Proxy oder eine Sicherheitssoftware auf deiner Seite das Gateway, das scheitert.

Wie behebe ich 502 Bad Gateway bei nginx?

Lies /var/log/nginx/error.log. Connection refused heißt, das Backend läuft nicht oder lauscht woanders; No such file or directory bei einer .sock-Datei heißt falscher Socket-Pfad, oft nach einem PHP-Upgrade; Permission denied deutet auf Socket-Rechte oder SELinux; prematurely closed heißt, das Backend ist mitten in der Anfrage abgestürzt oder wurde beendet. Behebe diese Ursache und lade nginx dann neu.