Sie öffnen Ihre Website und sehen statt der Seite einen weißen Bildschirm mit der Aufschrift 502 Bad Gateway, 504 Gateway Time-out, 403 Forbidden oder 500 Internal Server Error. Die Fehlernummer wirkt erschreckend, verrät aber ziemlich genau, an welcher Stelle des Weges vom Browser zur Website etwas kaputtgegangen ist, und die eigentliche Ursache steht in einer einzigen Zeile im Nginx-Protokoll. In diesem Artikel klären wir, wie dieser Weg aufgebaut ist, lernen, die Ursache in ein paar Minuten zu finden, und beheben nacheinander jeden der vier Fehler anhand der Beispiele, die am häufigsten vorkommen. Alle Beispiele haben wir auf einem Testserver mit Debian 12 nachgestellt, die Nginx-Version war zum Zeitpunkt der Prüfung 1.22.1, PHP 8.2.34.
Wie der Weg vom Browser zur Website aufgebaut ist
Wenn ein Besucher eine Seite öffnet, schickt sein Browser eine Anfrage an den Webserver, also an Nginx. Wird ein gewöhnliches Bild oder eine fertige HTML-Seite gebraucht, nimmt Nginx einfach die Datei von der Festplatte und liefert sie aus. Die meisten Websites bestehen aber nicht nur aus Dateien. Die Seite muss zusammengebaut werden, zum Beispiel mit einer Abfrage an die Datenbank, und das erledigt ein eigenes Programm, das man Backend nennt. Das kann PHP-FPM sein, das die WordPress-Skripte ausführt, oder eine Anwendung in Python oder Node.js, die auf ihrem eigenen Port lauscht. In dieser Kombination arbeitet Nginx als Pförtner. Er nimmt die Anfrage entgegen, reicht sie an das Backend weiter, wartet auf die Antwort und schickt sie an den Browser. Deshalb enthalten zwei der vier Fehler im Namen das Wort „Gateway“, denn in diesem Moment steht Nginx als Übergang zwischen dem Besucher und der Anwendung.
Die erste Ziffer des Codes zeigt, auf wessen Seite der Fehlschlag liegt. Der Code 403 gehört zur Familie 4xx, also zu Anfragen, die in der eingegangenen Form nicht ausgeführt werden können. Nginx hat sehr wohl verstanden, was der Browser will, weigert sich aber, die Antwort auszuliefern, weil es nichts zu zeigen gibt oder ihm die Rechte fehlen, die nötigen Dateien zu lesen. Die Codes der Familie 5xx bedeuten einen Fehler auf der Serverseite. Der Code 500 sagt, dass bei der Verarbeitung der Anfrage etwas kaputtgegangen ist, meistens im Skript oder in der Anwendung selbst. Der Code 502 bedeutet, dass Nginx vom Backend keine brauchbare Antwort bekommen hat, zum Beispiel weil das Backend nicht läuft. Der Code 504 bedeutet, dass das Backend zwar erreichbar ist, aber nicht in der vorgesehenen Zeit geantwortet hat. Bei jedem dieser Fehler müssen Sie deshalb zuerst herausfinden, an welchem Glied der Kette die Verbindung abgerissen ist.
Der Code nennt das Glied, aber nicht die Ursache, und die Ursache müssen Sie im Protokoll suchen. Nehmen wir zum Beispiel an, dass PHP-FPM angehalten wurde. Der Browser wendet sich weiterhin an Nginx, der versucht, die Anfrage an den Socket von PHP-FPM weiterzugeben, sich aber nicht verbinden kann. Deshalb zeigt er selbst eine kurze 502-Seite mit seiner Versionsnummer am unteren Rand an und schreibt ins Protokoll, womit genau er sich nicht verbinden konnte. Läuft PHP-FPM dagegen und das Skript ist mit einem fatalen Fehler abgestürzt, erhält Nginx von ihm eine Antwort mit dem Code 500 und leerem Inhalt und gibt sie unverändert weiter, während die Fehlermeldung dann der Browser selbst zeichnet. Beide Bilder haben wir auf dem Testserver gesehen, und am Aussehen der Seite lässt sich erraten, wer sie ausgegeben hat, Nginx oder die Anwendung. Die genaue Antwort steht dennoch im Fehlerprotokoll, zu dem wir gleich kommen.
Was Sie brauchen
- Einen Server mit Debian 12, auf dem Nginx bereits läuft, zum Beispiel den Tarif DE-RZ9-1. Die Beispiele im Artikel sind sehr leicht, und welcher Tarif für Ihre Website passt, hilft Ihnen der Artikel über VPS zu entscheiden. Wenn Sie noch keinen Server haben, bestellen Sie ihn nach der Anleitung.
- Eine SSH-Verbindung, zum Beispiel über Termius. Die Befehle werden als
rootausgeführt. - Nginx und PHP-FPM. Wie Sie beides installieren, haben wir im Artikel über die Installation von Nginx beschrieben. PHP-FPM brauchen Sie nur für die Beispiele mit PHP.
- Eine Website oder Anwendung, auf der Sie den Fehler sehen.
Adressen und Namen in den Beispielen
Auf dem Testserver lief jedes Beispiel auf einem eigenen Port, deshalb steht in den Befehlen und auf den Terminal-Screenshots statt einer Domain eine Adresse der Form 127.0.0.1:8091. Auf Ihre Website verweisen Sie über die Domain, zum Beispiel https://example.com. Die Ordner errors-demo-… und der Dienst errors-app sind ebenfalls für den Artikel erfunden, bei Ihnen heißen sie anders. Die Browser-Screenshots stammen von denselben Testwebsites, die Domain example.com in der Adresszeile ist fiktiv.
Schritt 1. Ermitteln Sie den Antwortcode
Beginnen Sie mit dem Befehl curl. Er greift auf die Website genauso zu wie ein Browser, zeigt aber nur das, was der Server tatsächlich geantwortet hat. Der Parameter -I fordert nur die Antwort-Header an, und der Code steht in der allerersten Zeile.
# Antwort-Header anzeigen, in der ersten Zeile steht der Code
curl -I https://example.comWenn Sie nur die Zeile mit dem Code brauchen, fügen Sie den Parameter -s hinzu und leiten Sie die Ausgabe an den Befehl head weiter.
# Nur die erste Zeile der Antwort
curl -sI https://example.com | head -1Eine Zeile wie HTTP/1.1 502 Bad Gateway bestätigt, dass der Fehler vom Server kam und nicht von Ihrem Netzwerk oder Browser. Außerdem ist im Header Server die Nginx-Version zu sehen.
Schritt 2. Lesen Sie das Fehlerprotokoll von Nginx
Jede Ablehnung schreibt Nginx in die Datei /var/log/nginx/error.log. Öffnen Sie das Protokoll im Beobachtungsmodus und aktualisieren Sie dann im Browser die Seite mit dem Fehler. Die neue Zeile erscheint im selben Moment.
# Protokoll live verfolgen, Beenden mit Ctrl+C
tail -f /var/log/nginx/error.logAlte Zeilen stören nicht
Der Befehl tail -f zeigt zuerst die letzten zehn Zeilen des Protokolls und wartet erst danach auf neue. Damit alte Einträge nicht ablenken, fügen Sie den Parameter -n 0 hinzu, wie auf dem Screenshot unten.

Der Antwortcode und die Zeile aus dem Protokoll, die die Ursache erklärt
Eine solche Zeile lässt sich leicht auseinandernehmen. Nehmen wir die, die wir erhalten haben, als die Anwendung hinter Nginx angehalten war.
2026/10/21 14:53:33 [error] 124894#124894: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:8098/", host: "example.com"- Uhrzeit und Schweregrad am Anfang der Zeile. Das Wort in eckigen Klammern zeigt den Schweregrad des Eintrags, bei uns kamen
[error]und[crit]vor. - Kern des Problems gleich nach der Prozessnummer. Hier ist das
connect() failed (111: Connection refused), also konnte Nginx sich nicht verbinden, weil der Port geschlossen ist. - client zeigt, wer die Anfrage gestellt hat, server und host zeigen, an welche Website, und request enthält die Anfrage selbst.
- upstream zeigt, wohin genau Nginx die Anfrage weitergeben wollte. Das ist die Adresse des Backends, und sie verrät, welches Programm Sie als Nächstes prüfen sollten.
Das Protokoll ist für alle Websites auf dem Server gemeinsam, deshalb suchen Sie bei viel Traffic den gewünschten Eintrag am besten anhand der Seitenadresse.
# Letzte Einträge, in denen die gesuchte Adresse vorkommt
grep '/slow' /var/log/nginx/error.log | tail -n 5Mehr darüber, wie man Logs liest und wo sie liegen, steht im Artikel über Logs unter Linux.
Schritt 3. Prüfen Sie das Programm hinter Nginx
Wenn in der Protokollzeile das Wort upstream steht, müssen Sie als Nächstes klären, was mit dem Backend los ist. Für eine Anwendung, die als systemd-Dienst läuft, prüfen Sie den Zustand und die letzten Meldungen. Setzen Sie statt myapp den Namen Ihres Dienstes ein, bei PHP ist das php8.2-fpm.
# Zustand des Dienstes
systemctl status myapp --no-pager
# Letzte 50 Meldungen des Dienstes
journalctl -u myapp -n 50 --no-pager
# Wer lauscht auf dem gewünschten Port, setzen Sie statt 8098 Ihren eigenen ein
ss -tlnp | grep 8098In der Zeile Active sollte active (running) stehen. Die Werte failed und inactive (dead) bedeuten, dass das Programm angehalten hat, und die Ursache nennt in der Regel das Protokoll des Dienstes. Der letzte Befehl zeigt die Programme, die auf Ports lauschen. Eine leere Ausgabe heißt, dass auf diesem Port niemand lauscht und Nginx niemanden hat, an den er Anfragen weitergeben kann. Wie Sie Dienste verwalten und ihre Logs lesen, ist ausführlich im Artikel über systemd-Dienste beschrieben.

Der Dienst ist im Zustand failed, im Protokoll steht die Ursache, auf Port 8098 lauscht niemand
Auf dem Screenshot ist der Dienst errors-app gleich nach dem Start abgestürzt. Im Protokoll ist die Ursache zu sehen, FileNotFoundError, also fehlte der Anwendung die Konfigurationsdatei. Mit der Fehlernummer und der Zeile aus dem Protokoll können Sie zur Analyse der konkreten Ursache übergehen.
Fehler 502 Bad Gateway
Die Seite 502 bedeutet, dass Nginx die Anfrage angenommen hat, aber keine Antwort vom Backend bekommen konnte. Sie sieht aus wie eine kurze weiße Seite mit der Nginx-Versionsnummer am unteren Rand.

So sieht der Fehler 502 im Browser aus
Wir haben die drei häufigsten Ursachen nachgestellt, und jede hinterlässt im Protokoll eine eigene Zeile.
Die Anwendung läuft nicht
Das ist die einfachste und häufigste Ursache. In den Nginx-Einstellungen gibt die Website Anfragen an die Anwendung weiter.
location / {
proxy_pass http://127.0.0.1:8098;
}Wenn die Anwendung angehalten wurde, abgestürzt ist oder nach einem Neustart des Servers nie gestartet wurde, lauscht auf ihrem Port niemand. Das Protokoll enthält dann die schon bekannte Zeile.
2026/10/21 14:53:33 [error] 124894#124894: *3 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:8098/", host: "example.com"Der Code 111: Connection refused bedeutet wörtlich, dass die Verbindung abgelehnt wurde, weil der Port geschlossen ist. In unserem Beispiel hat die Ursache das Protokoll des Dienstes gezeigt, das Sie auf dem Screenshot oben gesehen haben. Die Anwendung ist nicht gestartet, weil wir ihre Konfigurationsdatei umbenannt hatten. Legen Sie die Datei zurück und starten Sie den Dienst.
# Konfigurationsdatei an ihren Platz zurücklegen
mv /opt/errors-demo/config.json.old /opt/errors-demo/config.json
# Dienst starten und die Antwort der Website prüfen
systemctl start errors-app
curl -I http://127.0.0.1:8091
Der Dienst läuft, die Website antwortet wieder mit dem Code 200
Damit sich ein solcher Fehler nicht nach jedem Absturz wiederholt, überlassen Sie es systemd, die Anwendung selbst wieder hochzufahren. Fügen Sie dazu in der Dienstdatei zwei Zeilen im Abschnitt [Service] hinzu.
[Service]
# Anwendung neu starten, wenn sie abgestürzt ist
Restart=on-failure
# Vor dem Neustart drei Sekunden warten
RestartSec=3Laden Sie danach die Einstellungen neu und aktivieren Sie den Autostart beim Start des Servers.
# Änderungen in der Dienstdatei übernehmen
systemctl daemon-reload
# Start zusammen mit dem Server aktivieren
systemctl enable errors-appDie Prüfung war anschaulich. Wir haben den Prozess mit dem Befehl systemctl kill -s KILL errors-app zwangsweise beendet. Gleich danach antwortete die Website mit dem Code 502, und nach drei Sekunden startete systemd die Anwendung neu und die Website öffnete sich wieder. Beachten Sie, dass vor dem Aktivieren des Autostarts in der Zeile Loaded auf dem Screenshot das Wort disabled stand. Es bedeutet, dass der Dienst nach einem Neustart des Servers nicht von selbst startet.
PHP-FPM, Socket nicht gefunden
Bei PHP-Websites gibt Nginx Anfragen nicht an einen Port weiter, sondern an einen Socket, also eine spezielle Datei, über die er mit PHP-FPM kommuniziert. Der Pfad dazu steht in der Direktive fastcgi_pass.
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}Gibt es diese Datei nicht, antwortet Nginx mit 502, und das Protokoll sagt das im Klartext.
2026/10/21 14:53:33 [crit] 124894#124894: *5 connect() to unix:/run/php/php8.1-fpm.sock failed (2: No such file or directory) while connecting to upstream, client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.1-fpm.sock:", host: "example.com"Der Code 2: No such file or directory bedeutet, dass die Socket-Datei nicht existiert. Meist gibt es zwei Ursachen. Entweder steht in den Einstellungen der Name einer anderen PHP-Version, zum Beispiel aus einer Anleitung für PHP 8.1, während bei Ihnen 8.2 installiert ist, oder der Dienst PHP-FPM ist angehalten, und sein Socket ist mit ihm verschwunden. Sehen Sie nach, welche Sockets es auf dem Server gibt.
# Liste der PHP-FPM-Sockets
ls /run/php/
In den Einstellungen steht php8.1-fpm.sock, auf dem Server gibt es aber nur php8.2-fpm.sock
In unserem Beispiel lagen im Ordner php8.2-fpm.sock und php-fpm.sock. Korrigieren Sie den Namen in fastcgi_pass, prüfen Sie die Einstellungen und wenden Sie sie an.
# Version im Socket-Namen ändern
sed -i 's/php8.1-fpm.sock/php8.2-fpm.sock/' /etc/nginx/sites-available/example.com
# Einstellungen prüfen und anwenden
nginx -t && systemctl reload nginxDie Datei php-fpm.sock
Unter Debian liegt neben dem Socket der jeweiligen Version noch php-fpm.sock. Das ist ein Link auf den Socket der installierten PHP-Version, und auf unserem Server führte er zu php8.2-fpm.sock. Wenn Sie in fastcgi_pass genau ihn angeben, müssen Sie den Namen in den Einstellungen beim Wechsel der PHP-Version nicht ändern. Wir haben geprüft, dass die Website mit einem solchen Pfad normal antwortet.
Stimmt der Name, der Socket fehlt aber trotzdem, ist PHP-FPM selbst angehalten. Wir haben ihn mit dem Befehl systemctl stop php8.2-fpm gestoppt und denselben Fehler 502 mit der Zeile connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory) erhalten. Der Befehl systemctl status php8.2-fpm zeigte inactive (dead), und nach dem Start des Dienstes funktionierte die Website wieder.

PHP-FPM ist gestoppt, der Socket fehlt, nach dem Start des Dienstes öffnet sich die Website wieder
Startet der Dienst nicht, zeigt journalctl -u php8.2-fpm -n 50 --no-pager die Ursache. Und nach Änderungen an den PHP-FPM-Einstellungen prüfen Sie diese mit dem Befehl php-fpm8.2 -t, wie wir weiter unten zeigen.
PHP-FPM, kein Zugriff auf den Socket
Manchmal ist der Socket vorhanden, aber Nginx kann ihn nicht nutzen. Im Protokoll steht dann ein anderer Code.
2026/10/21 14:57:38 [crit] 133790#133790: *56 connect() to unix:/run/php/errors-fpm.sock failed (13: Permission denied) while connecting to upstream, client: 127.0.0.1, server: example.com, request: "HEAD / HTTP/1.1", upstream: "fastcgi://unix:/run/php/errors-fpm.sock:", host: "127.0.0.1:8099"
Der Socket ist vorhanden, gehört aber root, und Nginx kann sich nicht mit ihm verbinden
Der Code 13: Permission denied bedeutet, dass Nginx keine Rechte für diese Datei hat. Nginx läuft unter dem Benutzer www-data, und den Zugriff auf den Socket bestimmen die Parameter listen.owner, listen.group und listen.mode in den Einstellungen des PHP-FPM-Pools. Laut PHP-Dokumentation erhält der Socket, wenn kein Besitzer angegeben ist, den Benutzer, unter dem der Dienst gestartet wurde, und die Rechte 0660. In unserem Beispiel haben wir einen eigenen Pool errors in der Datei /etc/php/8.2/fpm/pool.d/errors.conf angelegt und keinen Besitzer angegeben.
[errors]
user = nobody
group = nogroup
listen = /run/php/errors-fpm.sock
pm = ondemand
pm.max_children = 2
pm.process_idle_timeout = 10sDer Dienst läuft als root, deshalb erhielt der Socket den Besitzer root und die Rechte srw-rw----, und www-data gehört nicht zu dieser Gruppe. Fügen Sie dem Pool zwei Zeilen mit dem Besitzer hinzu.
# In den Ordner mit den Pool-Einstellungen wechseln
cd /etc/php/8.2/fpm/pool.d
# Dem Nginx-Benutzer die Arbeit mit dem Socket erlauben
printf 'listen.owner = www-data\nlisten.group = www-data\n' >> errors.conf
# Einstellungen prüfen und PHP-FPM neu starten
php-fpm8.2 -t && systemctl restart php8.2-fpm
# Besitzer des Sockets ansehen und die Website prüfen
ls -l /run/php/errors-fpm.sock
curl http://127.0.0.1:8099/
Der Socket gehört www-data, die Website antwortet
Hier hilft nur ein restart
Als wir nach der Änderung systemctl reload php8.2-fpm ausgeführt haben, blieb der Besitzer des Sockets derselbe, root, und der Fehler 502 verschwand nicht. Den Besitzer wechselte der Socket erst nach systemctl restart php8.2-fpm.
Prüfen Sie die Einstellungen immer vor dem Neuladen
Gewöhnen Sie sich an, vor jedem Befehl reload auch nginx -t auszuführen. Steckt in den Einstellungen ein Fehler, zeigt ihn die Prüfung an, und der laufende Nginx antwortet weiter mit der alten Konfiguration. Für PHP-FPM gibt es dieselbe Prüfung, den Befehl php-fpm8.2 -t.
Fehler 504 Gateway Time-out
Die Seite 504 besagt, dass das Backend lebt und die Anfrage angenommen hat, aber zu lange an der Antwort arbeitet. Nginx wartet darauf nur begrenzte Zeit und bricht das Warten, wenn nichts kommt, selbst ab. Die Wartezeit legt die Direktive proxy_read_timeout fest, standardmäßig sind das 60 Sekunden, und für PHP-FPM übernimmt fastcgi_read_timeout dieselbe Rolle. Ein wichtiges Detail aus der Nginx-Dokumentation ist, dass diese Zeit zwischen zwei aufeinanderfolgenden Lesevorgängen gezählt wird und nicht für die gesamte Antwort. Schickt das Backend den nächsten Datenabschnitt öfter als alle 60 Sekunden, reißt die Verbindung nicht ab.
Um das mit eigenen Augen zu sehen, haben wir der Testanwendung die Adresse /slow hinzugefügt, die erst nach 75 Sekunden antwortet, wie ein aufwendiger Bericht oder eine lange Datenbankabfrage. Die Seite lud genau eine Minute lang, dann erschien der Fehler.

Der Fehler 504 erscheint nach einer Minute Wartezeit
Mit dem Parameter -w kann der Befehl curl die Zeit messen und den Code auf einen Blick zeigen.
# Antwortcode und Wartezeit anzeigen
curl -s -o /dev/null -w '%{http_code} nach %{time_total} s\n' http://127.0.0.1:8093/slowIm Protokoll erschien dabei dieser Eintrag.
2026/10/21 15:04:14 [error] 144934#144934: *81 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 127.0.0.1, server: example.com, request: "GET /slow HTTP/1.1", upstream: "http://127.0.0.1:8098/slow", host: "127.0.0.1:8093"
Der Code 504 nach 60,06 Sekunden und die Zeile upstream timed out im Protokoll
Die Wörter while reading response header bedeuten, dass Nginx sich erfolgreich verbunden, die Anfrage gesendet und auf den Beginn der Antwort gewartet hat. Die Messung ergab 60,06 Sekunden, Nginx hat das Warten also genau nach dem Standardwert abgebrochen.
Einen 504 behebt man am besten an der Ursache. Finden Sie heraus, was genau lange dauert, zum Beispiel eine aufwendige Datenbankabfrage, der Zugriff auf einen fremden Dienst oder ein großer Bericht, und beschleunigen Sie es. Ist die Aufgabe von Natur aus langwierig, zum Beispiel ein Export oder Import, erhöhen Sie die Wartezeit für die betreffende Adresse. Fügen Sie dem Block location die Direktive proxy_read_timeout hinzu.
location / {
proxy_pass http://127.0.0.1:8098;
# Auf die Antwort der Anwendung bis zu zwei Minuten warten
proxy_read_timeout 120s;
}Prüfen Sie die Einstellungen, wenden Sie sie an und wiederholen Sie dann die Anfrage.
# Einstellungen prüfen und anwenden
nginx -t && systemctl reload nginx
# Anfrage wiederholen
curl -s -o /dev/null -w '%{http_code} nach %{time_total} s\n' http://127.0.0.1:8093/slow
Dieselbe Anfrage liefert jetzt 200 nach 75 Sekunden
Bei Websites mit PHP-FPM gehen Sie genauso vor, nur heißt die Direktive fastcgi_read_timeout und steht im Block location ~ \.php$ neben fastcgi_pass. Wir haben das an einem Skript geprüft, das nach 70 Sekunden antwortet. Mit den Standardeinstellungen lieferte es genau nach 60 Sekunden einen 504, und mit der Zeile fastcgi_read_timeout 120s; antwortete es nach 70 Sekunden mit dem Code 200. Die Zeile im Protokoll sah dabei so aus.
2026/10/21 15:07:46 [error] 153814#153814: *89 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 127.0.0.1, server: example.com, request: "GET /slow.php HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock", host: "127.0.0.1:8096"Das Nginx-Timeout hebt das PHP-Limit nicht auf
PHP hat eine eigene Obergrenze für die Laufzeit eines Skripts, den Parameter max_execution_time. In der Datei /etc/php/8.2/fpm/php.ini beträgt er auf unserem Server 30 Sekunden. Wir haben ein Skript gestartet, das ununterbrochen den Prozessor auslastete, und PHP stoppte es genau nach 30 Sekunden. Als Antwort kam kein 504, sondern der Fehler 500, und im Protokoll erschien die Zeile PHP Fatal error: Maximum execution time of 30 seconds exceeded in /var/www/errors-demo-php/busy.php on line 3. Ein Skript, das einfach mit dem Aufruf sleep(70) wartete, fiel nicht unter diese Beschränkung, weil laut PHP-Dokumentation die Wartezeit von Systemaufrufen unter Linux nicht mitgezählt wird. Stößt Ihr Skript genau an das PHP-Limit, erhöhen Sie max_execution_time und nicht nur das Timeout von Nginx.
Fehler 403 Forbidden
Die Seite 403 bedeutet, dass Nginx die Anfrage verstanden hat, sich aber weigert, sie zu beantworten. In der Praxis gibt es zwei Ursachen. Entweder gibt es im Ordner nichts zu zeigen, oder Nginx hat keine Rechte, die Datei zu lesen. Im Protokoll lassen sich diese Fälle leicht unterscheiden, beginnen Sie deshalb immer dort.

So sieht der Fehler 403 im Browser aus
Keine Indexdatei
Wenn ein Besucher die Adresse eines Ordners öffnet, zum Beispiel https://example.com/, sucht Nginx darin die Startseite. Den Dateinamen legt die Direktive index fest, standardmäßig ist das index.html. Gibt es eine solche Datei nicht, könnte Nginx die Dateiliste des Ordners anzeigen, aber standardmäßig ist das abgeschaltet (autoindex off), und deshalb antwortet er mit 403.
2026/10/21 14:53:33 [error] 124894#124894: *7 directory index of "/var/www/errors-demo-403a/" is forbidden, client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", host: "example.com"Die Formulierung directory index of … is forbidden bedeutet genau, dass im Ordner keine Indexdatei liegt und die Dateiliste verboten ist. Sehen Sie nach, was im Ordner der Website liegt.
# Inhalt des Website-Ordners anzeigen
ls -la /var/www/errors-demo-403a
Im Website-Ordner gibt es keine Startseite, deshalb antwortet Nginx mit 403
Der Ordner war leer. Meist passiert das, wenn die Website in den falschen Ordner hochgeladen oder die Datei der Startseite selbst vergessen wurde. Prüfen Sie, dass die Direktive root auf den Ordner zeigt, in dem die Website tatsächlich liegt, und dass dort eine Datei mit dem Namen aus der Direktive index vorhanden ist. Ist das bei einer PHP-Website index.php, stellen Sie sicher, dass sie in index index.html index.php; angegeben ist. Nach dem Hochladen der Datei ändert sich die Antwort sofort, Nginx müssen Sie nicht neu laden.
# Startseite anlegen
echo '<h1>Website funktioniert</h1>' > /var/www/errors-demo-403a/index.html
# Antwort prüfen
curl -sI http://127.0.0.1:8094/ | head -1Danach antwortete der Server mit HTTP/1.1 200 OK.
Brauchen Sie ausdrücklich eine Dateiliste, zum Beispiel für einen Ordner mit Archiven, schalten Sie sie mit der Direktive autoindex on; im gewünschten Block location ein. Wir haben geprüft, dass Nginx in diesem Fall die Seite Index of / mit der Liste der Dateien im Ordner ausliefert.
Die Dateiliste sehen alle
Bei autoindex on sieht jeder Besucher die Namen aller Dateien im Ordner und kann jede davon herunterladen. Schalten Sie sie nur für Ordner ein, die Sie vollständig zeigen möchten.
Keine Rechte für die Datei
Im zweiten Fall liegt die Datei am richtigen Platz, aber Nginx kann sie nicht lesen. Das Protokoll meldet das so.
2026/10/21 14:53:33 [error] 124894#124894: *8 open() "/var/www/errors-demo-403b/index.html" failed (13: Permission denied), client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", host: "example.com"Der Code 13: Permission denied bedeutet eine Zugriffsverweigerung auf der Ebene von Linux. Um zu verstehen, auf wessen Rechte es ankommt, finden Sie heraus, unter welchem Benutzer Nginx läuft, und sehen Sie sich dann mit dem Befehl namei die Rechte für jedes Element des Pfades an.
# Unter welchem Benutzer Nginx läuft
grep ^user /etc/nginx/nginx.conf
# Rechte für jeden Ordner und jede Datei auf dem Pfad
namei -l /var/www/errors-demo-403b/index.html
Nginx läuft unter www-data, und die Datei ist für alle außer root gesperrt
Nginx läuft unter www-data, und die Datei hat die Rechte -rw------- und gehört root, also kann nur der Besitzer sie lesen. Für www-data ist das ein fremder Benutzer, und er hat keinen Zugriff. Dieses Bild haben wir erhalten, indem wir die Datei als root mit den Rechten 600 angelegt haben. Für eine Website gilt eine einfache Regel. Ordner erhalten 755, Dateien 644, und als Besitzer wird der Benutzer des Webservers eingetragen. Wie Rechte aufgebaut sind und was diese Zahlen bedeuten, steht im Artikel über Zugriffsrechte unter Linux.
# Die Website an den Benutzer des Webservers übergeben
chown -R www-data:www-data /var/www/errors-demo-403b
# Ordner 755, Dateien 644
find /var/www/errors-demo-403b -type d -exec chmod 755 {} +
find /var/www/errors-demo-403b -type f -exec chmod 644 {} +
Die Datei ist lesbar, die Website antwortet mit dem Code 200
Zum Lesen der Datei genügt Nginx das Recht r für „Andere“. Wir haben geprüft, dass die Website auch mit dem Besitzer root und den Rechten 644 aufgerufen werden kann, und der Wechsel des Besitzers ist nötig, damit die Website hochgeladene Dateien selbst speichern kann.
Beheben Sie 403 nicht mit den Rechten 777
Die Rechte 777 erlauben allen Benutzern des Servers, die Datei zu lesen und zu ändern, und der Fehler verschwindet tatsächlich. Mit ihm verschwindet aber auch der Schutz der Website, deshalb sollten Sie einen solchen Ratschlag aus dem Internet besser nicht befolgen. Machen Sie den Benutzer www-data zum Besitzer und setzen Sie 755 und 644.
Keine Rechte für einen Ordner auf dem Weg zur Datei
Manchmal ist die Datei selbst für alle zugänglich, und der Fehler bleibt trotzdem bestehen. Um die Datei zu erreichen, muss Nginx jeden Ordner auf dem Pfad durchlaufen, und dafür braucht www-data auf jedem davon das Recht x. Es genügt, dass ein einziger Ordner gesperrt ist. Das Protokoll sieht in diesem Fall etwas anders aus, es enthält nicht das Wort open().
2026/10/21 15:08:10 [error] 156517#156517: *109 "/var/www/errors-demo-403c/public/index.html" is forbidden (13: Permission denied), client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", host: "127.0.0.1:8095"In unserem Beispiel hat die Datei index.html die Rechte 644, aber der Ordner darüber ist gesperrt. Beim Finden hilft derselbe Befehl namei.
# Den gesperrten Ordner auf dem Pfad zur Datei suchen
namei -l /var/www/errors-demo-403c/public/index.html
Der Ordner errors-demo-403c ist für alle außer root gesperrt, nach chmod 755 öffnet sich die Website
In der Ausgabe hat der Ordner errors-demo-403c die Rechte drwx------, die übrigen Ordner sind offen. Öffnen Sie ihn zum Lesen und Betreten.
# Allen Benutzern erlauben, den Ordner zu betreten
chmod 755 /var/www/errors-demo-403cEin solches Bild ist typisch für Websites, die in einem gesperrten Ordner liegen, zum Beispiel im Home-Verzeichnis. Auf unserem Server hat der Ordner /root die Rechte 700, deshalb sollten Sie eine Website dort nicht ablegen. Verwenden Sie für Websites /var/www.
Fehler 500 Internal Server Error
Der Code 500 gilt als der unbestimmteste, denn er bedeutet einfach „bei der Verarbeitung der Anfrage ist etwas schiefgegangen“. Deshalb ist es hier besonders wichtig, ins Protokoll zu schauen. Wir haben zwei Ursachen nachgestellt, die häufiger vorkommen als andere. Die erste betrifft PHP, die zweite Nginx selbst.
Fataler Fehler in PHP
Stürzt ein PHP-Skript ab, antwortet PHP-FPM mit dem Code 500. Standardmäßig zeigt PHP dem Besucher den Fehlertext nicht, und in unserem Fall war der Parameter display_errors in /etc/php/8.2/fpm/php.ini ausgeschaltet und log_errors eingeschaltet. Der Inhalt der Antwort war leer, deshalb hat die Aufschrift der Browser selbst gezeichnet und nicht Nginx.

Bei einer leeren Antwort zeigt der Browser seine eigene Seite mit dem Code HTTP ERROR 500
Dafür landet alles, was PHP sagen wollte, im Protokoll von Nginx.
2026/10/21 15:09:26 [error] 156517#156517: *123 FastCGI sent in stderr: "PHP message: PHP Fatal error: Uncaught Error: Call to undefined function mb_strlen() in /var/www/errors-demo-php/page.php:3
Stack trace:
#0 {main}
thrown in /var/www/errors-demo-php/page.php on line 3" while reading response header from upstream, client: 127.0.0.1, server: example.com, request: "GET /page.php HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock:", host: "127.0.0.1:8096"
Der Code 500 und die Zeile aus dem Protokoll mit dem PHP-Fehlertext
Der Eintrag beginnt mit den Worten FastCGI sent in stderr, danach folgt die PHP-Meldung selbst. Lesen müssen Sie sie so. Der Fehlertyp PHP Fatal error, dann der Kern, Call to undefined function mb_strlen(), es wurde also eine Funktion aufgerufen, die es nicht gibt, und am Ende die Datei und die Zeile, in der das geschah. In unserem Beispiel gehört die Funktion mb_strlen() zum Modul mbstring, das bei einer frischen PHP-Installation fehlt. Dieses Modul steht auf der Liste, die wir im Artikel über die Installation von Nginx für WordPress empfehlen. Installieren Sie das fehlende Modul.
# Modul mbstring für PHP installieren
apt install -y php-mbstringNach der Installation haben wir nichts neu gestartet, und die Seite funktionierte sofort.

Das Modul ist installiert, die Seite liefert 200 und das Ergebnis der Funktion
Eine weitere häufige Ursache für 500 in PHP ist ein Syntaxfehler nach dem Bearbeiten einer Datei. Eine Datei können Sie auf dem Server mit dem Befehl php -l prüfen, und er sagt auch, in welcher Zeile der Fehler steht.
# Syntax der Datei prüfen
php -l /var/www/errors-demo-php/parse.phpPHP Parse error: syntax error, unexpected token "echo", expecting "," or ";" in parse.php on line 3
Errors parsing parse.phpSchalten Sie die Fehleranzeige auf einer produktiven Website nicht ein
Den Parameter display_errors können Sie einschalten, um den Fehler direkt im Browser zu sehen, aber auf einer produktiven Website sollten Sie das nicht tun. Der Fehlertext enthält Pfade zu Dateien auf dem Server, und jeder Besucher sieht ihn. Das Nginx-Protokoll liefert dieselben Informationen, aber nur Ihnen.
Zyklus interner Weiterleitungen
Die zweite Ursache hat mit der Anwendung gar nichts zu tun, es ist ein Fehler in den Einstellungen von Nginx selbst. Betrachten wir eine Einstellung, bei der für jede Adresse, zu der es keine passende Datei gibt, die Startseite /index.html ausgeliefert wird.
root /var/www/errors-demo-cycle;
location / {
try_files $uri /index.html;
}Der letzte Parameter von try_files funktioniert wie eine interne Weiterleitung. Nginx beginnt die Verarbeitung noch einmal, jetzt für die Adresse /index.html, und landet wieder im selben location. Existiert die Datei index.html, endet alles erfolgreich. Gibt es sie nicht, zum Beispiel weil man vergessen hat, den Build der Website hochzuladen, oder root falsch zeigt, leitet Nginx die Anfrage wieder auf /index.html um, und so immer im Kreis. Laut Nginx-Dokumentation wird ein solcher Zyklus nach zehn Versuchen abgebrochen, und der Client erhält den Fehler 500.

Hier hat Nginx den Fehler 500 selbst gezeichnet, deshalb ist unten seine Version zu sehen
Im Protokoll sagt der Eintrag das direkt.
2026/10/21 14:53:33 [error] 124894#124894: *11 rewrite or internal redirection cycle while internally redirecting to "/index.html", client: 127.0.0.1, server: example.com, request: "GET / HTTP/1.1", host: "example.com"Die Formulierung rewrite or internal redirection cycle bedeutet eine zyklische Weiterleitung, und in Anführungszeichen steht die Adresse, auf die Nginx im Kreis gelaufen ist. Prüfen Sie, ob eine solche Datei im Ordner liegt, der in root angegeben ist, und laden Sie sie bei Bedarf hoch.
# Nachsehen, was im Website-Ordner liegt
ls -la /var/www/errors-demo-cycle
# Datei an ihren Platz legen
echo '<h1>Website funktioniert</h1>' > /var/www/errors-demo-cycle/index.html
# Antwort prüfen
curl -sI http://127.0.0.1:8097/ | head -1
Der Zyklus endet, sobald die benötigte Datei im Website-Ordner liegt
Nach dem Anlegen der Datei lautete die Antwort HTTP/1.1 200 OK. Nginx musste dafür nicht neu geladen werden, denn korrigiert werden musste nicht die Konfiguration, sondern der Inhalt des Ordners.
Häufige Fragen
upstream prematurely closed connection while reading response header from upstream
Diese Zeile im Protokoll begleitet den Fehler 502. Nginx hat sich mit der Anwendung verbunden und die Anfrage gesendet, aber die Anwendung hat die Verbindung abgebrochen, ohne eine Antwort zu schicken. Sie ist zum Beispiel mitten in der Verarbeitung der Anfrage abgestürzt. Sehen Sie sich das Protokoll des Dienstes mit dem Befehl journalctl -u dienstname -n 50 --no-pager an, dort sollte die Ursache stehen. Wir haben das mit einer Zeile der Form upstream prematurely closed connection while reading response header from upstream, … request: "GET /close HTTP/1.1", upstream: "http://127.0.0.1:8098/close" nachgestellt.
upstream sent too big header while reading response header from upstream
Der Fehler 502 tritt auf, wenn die Anwendung zu lange Antwort-Header schickt, zum Beispiel riesige Cookies. Nginx liest den ersten Teil der Antwort in einen Puffer, dessen Größe standardmäßig einer Speicherseite entspricht, also 4 oder 8 KB. Passen die Header nicht hinein, gilt die Antwort als ungültig. Wir haben das an einem Header mit 9000 Zeichen Länge geprüft. Vergrößern Sie den Puffer im Block location.
location / {
proxy_pass http://127.0.0.1:8098;
# Größerer Puffer für die Antwort-Header
proxy_buffer_size 16k;
proxy_buffers 8 16k;
}Die Zeile proxy_buffers 8 16k; dürfen Sie nicht weglassen. Als wir nur proxy_buffer_size 16k; hinzugefügt hatten, meldete nginx -t den Fehler "proxy_busy_buffers_size" must be less than the size of all "proxy_buffers" minus one buffer, und solche Einstellungen dürfen nicht angewendet werden. Mit beiden Zeilen bestand die Prüfung, und die Seite öffnete sich mit dem Code 200.
nginx -t meldet [emerg] nach einer Änderung der Einstellungen
Das bedeutet, dass in der Konfigurationsdatei ein Fehler steckt und Nginx eine solche Konfiguration nicht anwendet. Solange Sie systemctl reload nginx nicht ausführen, arbeitet die Website mit den alten Einstellungen weiter, deshalb führen Sie immer nginx -t && systemctl reload nginx aus und nicht nur den Befehl reload. Der Text der Meldung zeigt, was genau nicht stimmt. Zum Beispiel weist "proxy_busy_buffers_size" must be less than the size of all "proxy_buffers" minus one buffer auf nicht aufeinander abgestimmte Puffergrößen im Block location hin.
Die Website zeigt einen Fehler, aber error.log ist leer
Höchstwahrscheinlich hat diese Website ein eigenes Fehlerprotokoll. Es wird mit der Direktive error_log im Block server festgelegt, und dann landen die Einträge in der angegebenen Datei und nicht im allgemeinen error.log. Wir haben das geprüft. Mit der Direktive error_log /var/log/nginx/errors-demo.log; im Block der Website erschien die Zeile zum Fehler 403 nur in dieser Datei. Alle Protokolle, die Nginx kennt, finden Sie mit dem folgenden Befehl.
# Alle error_log-Direktiven in allen Einstellungen anzeigen
nginx -T 2>/dev/null | grep -n error_logIn unserem Beispiel gab der Befehl error_log /var/log/nginx/error.log; aus der Hauptdatei und die Zeile mit der Adresse des Website-Protokolls aus.
curl: (7) Failed to connect to … port … Couldn't connect to server
Das ist bereits keiner der vier Fehler mehr. Nginx hat überhaupt keine Antwort geschickt, weil auf diesem Port niemand lauscht. Der Browser zeigt in diesem Fall seine eigene Seite, bei Chrome ist das ERR_CONNECTION_REFUSED. Diese Meldung haben wir beim Zugriff auf einen Port erhalten, den niemand belegt hatte, curl: (7) Failed to connect to 127.0.0.1 port 8090 after 0 ms: Couldn't connect to server. Prüfen Sie mit dem Befehl systemctl status nginx, dass Nginx selbst läuft, und dass die Ports 80 und 443 in der Firewall geöffnet sind.
PHP-FPM schreibt ins Protokoll Maximum execution time of 30 seconds exceeded
Das Skript lief länger, als der Parameter max_execution_time erlaubt, standardmäßig 30 Sekunden, und PHP hat es gestoppt. Nginx gibt dabei 500 zurück und nicht 504. Öffnen Sie /etc/php/8.2/fpm/php.ini, erhöhen Sie den Wert des Parameters und starten Sie den Dienst mit dem Befehl systemctl restart php8.2-fpm neu. Muss das Skript länger als eine Minute laufen, vergessen Sie nicht, auch fastcgi_read_timeout in den Nginx-Einstellungen zu erhöhen.
Unsere Ressourcen
Telegram-Kanal: https://t.me/f3cloud_news
Hosting-Website: https://f3cloud.com