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.
# 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 siehst | Gesendet von | Was es meist bedeutet |
|---|---|---|
Weiße Seite, 502 Bad Gateway, Fußzeile nginx | nginx | Backend 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_proxy | Die Antwort des Backends brach ab oder war kein HTTP (AH01102) |
| 502 Bad Gateway. The server returned an invalid or incomplete response. | HAProxy | Der Server im Backend hat nichts Brauchbares gesendet |
| Leere Seite mit Status 502 | Caddy | Sein Log nennt den Verbindungsfehler, etwa connect: connection refused |
Bad Gateway | Traefik | Keine Antwort vom Dienst oder Container |
HTTP Error 502.3 oder 502.5 Process Failure | IIS (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-Design | Das Netzwerk von Cloudflare selbst | Ein Problem innerhalb von Cloudflare; prüfe die Statusseite |
| Schlichte 502-Seite, während die Website hinter Cloudflare läuft | Der Proxy deines Origin-Servers, durchgereicht | Alles 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:
| Code | Was 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-Meldung | Ursache | Lösung |
|---|---|---|
connect() failed (111: Connection refused) while connecting to upstream | Auf dieser Adresse und diesem Port lauscht nichts: Die App ist aus, abgestürzt oder lauscht woanders | Starten, 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 gestoppt | Pfad korrigieren |
(13: Permission denied) while connecting to upstream | nginx darf den Socket nicht öffnen, oder SELinux blockiert die Verbindung | Socket-Besitzer oder SELinux-Boolean |
upstream prematurely closed connection while reading response header from upstream | Das Backend ist abgestürzt oder hat die Anfrage beendet, bevor es antwortete | Absturz, OOM-Kill oder Worker-Timeout |
recv() failed (104: Connection reset by peer) while reading response header from upstream | Dieselbe Familie: Die Verbindung wurde mitten in der Anfrage zurückgesetzt | Log des Backends prüfen |
upstream sent too big header while reading response header from upstream | Die Response-Header (meist Cookies) sind größer als der Puffer des Proxys | Puffergröße erhöhen |
no live upstreams while connecting to upstream | Jeder Server im upstream-Block wurde als ausgefallen markiert | Backends 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:
# 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 5Existiert 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.
curl: (7) Failed to connect to localhost port 80 after 0 ms: Couldn't connect to serverPHP-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:
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: pongKommt 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:
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 NeustartsActive: 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.sockwirdphp8.3-fpm.sock, undfastcgi_passnennt noch den alten.ls /run/php/zeigt, was existiert. - localhost ist nicht eine einzige Adresse.
proxy_pass http://localhost:3000löst sowohl zu::1als auch zu127.0.0.1auf, 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 zu127.0.0.1, während eine Node-App (ab Version 17), die anlocalhostgebunden ist, nur auf::1lauscht. Das Feldupstream: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.1und proxye dorthin, oder setz den Proxy ins selbe Docker-Netzwerk und nutze den Dienstnamen.
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:
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = nginx ; die Gruppe des nginx-Worker-Benutzers
listen.mode = 0660Starte 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 alschild 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_timeoutvon PHP-FPM protokolliertexecution 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:
# 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 mitcurl -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.keepAliveTimeoutvon 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
| Frage | 502 Bad Gateway | 503 Service Unavailable | 504 Gateway Timeout |
|---|---|---|---|
| Wer sendet den Code | Ein Proxy oder Gateway | Der Server selbst oder ein Proxy | Ein Proxy oder Gateway |
| Was passiert ist | Die Antwort des Backends war ungültig, abgebrochen oder nicht zu bekommen | Der Server lehnt vorerst ab: Überlast, Wartung, ein Rate Limit | Das Backend hat die Anfrage angenommen, aber nicht rechtzeitig geantwortet |
| Typisches nginx-Log | connect() failed (111) | limiting requests | upstream timed out (110) |
| Retry-After-Header | Nein | Oft, bei geplanter Wartung | Nein |
| Erste Prüfung | Läuft das Backend, und lauscht es dort, wo der Proxy es erwartet? | Worker-Pool, Wartungsschalter, Rate Limits | Langsame 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-failurein 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 nginxstartet neue Worker und lässt die alten ihre Anfragen zu Ende bringen; ein Restart bricht sie ab. - Speicherreserve lassen. Bemiss
pm.max_childrenund 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.