Fehlerbehebung

400 Bad Request: was der Fehler 400 bedeutet, was der Server abgelehnt hat und wie du ihn behebst

Fehler 400 Bad Request heißt auf Deutsch „fehlerhafte Anfrage“: Der Server lehnt eine kaputte oder zu große Anfrage ab. Ursachen, Lösungen und curl-Tests.

Veröffentlicht ~9 Min. Lesezeit
Auf dieser Seite

Kurz gesagt: Der Fehler 400 Bad Request (auf Deutsch: „fehlerhafte Anfrage“) heißt, dass der Server die Anfrage nicht bearbeitet hat, weil etwas darin kaputt oder zu groß war: ein übergroßes Cookie, reines HTTP an einen HTTPS-Port, ein fehlender Host-Header oder ein Body, den die Anwendung nicht lesen konnte. Im Browser hilft meist, die Cookies für genau diese eine Website zu löschen. Auf deinem eigenen Server liest du die Zeile unter „400 Bad Request“ auf der Fehlerseite und danach das Fehlerlog von nginx auf Stufe info, das das genaue Problem nennt.

die ganze Antwort sehen, nicht nur den Status
curl -si https://example.com/path | head -n 20
# der <title> der nginx-Fehlerseite nennt den Grund, zum Beispiel
# <title>400 Request Header Or Cookie Too Large</title>

Was bedeutet „Bad Request“ auf Deutsch?

„Bad Request“ heißt wörtlich „fehlerhafte“ oder „ungültige Anfrage“. Der HTTP-Fehler 400 gehört zur Klasse 4xx, den Client-Fehlern: Laut RFC 9110 kann oder will der Server die Anfrage nicht bearbeiten, weil er etwas daran für einen Fehler des Clients hält, etwa eine kaputte Syntax oder einen ungültigen Aufbau der Nachricht. Anders als bei 401 (engl.) oder 403 (engl.) geht es nicht darum, wer du bist oder was du darfst, sondern darum, dass die Anfrage selbst so nicht angenommen wird.

„Client-Fehler“ heißt dabei nicht, dass immer der Besucher schuld ist. Der Server hat die Anfrage abgelehnt, die Ursache kann aber auch bei ihm liegen: ein Link, der http:// auf Port 443 schickt, oder ein Reverse Proxy, der der Anwendung einen Host übergibt, den sie nicht akzeptiert. Welcher Fall es ist, verrät die Meldung unter dem Status.

Was bedeutet die Meldung unter 400 Bad Request?

nginx nutzt für bestimmte Fehler ein paar interne Codes und schickt sie alle als 400 an den Client, mit dem Grund als zweiter Zeile. Der Text auf der Seite ist dein erster Hinweis:

Meldung auf der SeiteWas passiert istLösung
Request Header Or Cookie Too LargeEine Header-Zeile, meist Cookie, ist größer als 8 KB, oder alle Header zusammen passen nicht in die Puffer von nginxBesucher: die Cookies der Website löschen. Betreiber: large_client_header_buffers erhöhen und die Cookies verkleinern
The plain HTTP request was sent to HTTPS porthttp:// ging an einen Port, der TLS erwartet, meist 443https:// nutzen oder mit error_page 497 umleiten
No required SSL certificate was sentDer Server verlangt ein Client-Zertifikat (ssl_verify_client), und es wurde keins geschicktDas Client-Zertifikat installieren oder die Anforderung entfernen
Nur „400 Bad Request“ von nginxEine fehlerhafte Anfrage: fehlender, doppelter oder ungültiger Host-Header, kaputte AnfragezeileDas Fehlerlog auf Stufe info nennt den Grund
400 mit JSON-BodyDeine Anwendung hat die Eingabe abgelehnt: ungültiges JSON, ein fehlendes Feld, ein nicht erlaubter HostDen Body der Antwort lesen; er nennt meist das Feld

Zwei verwandte Codes sehen ähnlich aus. Eine Anfragezeile (Methode plus URL), die länger als ein 8-KB-Puffer ist, bekommt stattdessen 414 Request-URI Too Large, und Node.js beantwortet übergroße Header mit 431 Request Header Fields Too Large.

Wie behebe ich den Fehler 400 als Besucher?

Öffne dieselbe Seite in einem privaten Fenster bzw. Inkognito-Fenster. Lädt sie dort, sind die Cookies das Problem, die dein normales Fenster an diese Website schickt: zu viele davon, oder eines, das gewachsen oder beschädigt ist. Lösch die Cookies für genau diese Website über das Symbol für Website-Informationen links neben der Adressleiste und lade die Seite neu. Deinen ganzen Browserverlauf musst du dafür nicht löschen.

Scheitert auch das private Fenster, schau dir die URL an: Ein verirrtes %, ein doppelt codierter Link oder http:// zusammen mit :443 erzeugen alle einen 400. Auch Browsererweiterungen, die Header hinzufügen, können ihn auslösen. Hilft nichts davon, liegt das Problem beim Server, und nur sein Betreiber kann es beheben.

Wo protokolliert nginx den Grund für einen 400?

Das Access-Log zeigt den 400, aber nicht den Grund. nginx schreibt den Grund auf Stufe info ins Fehlerlog, und die Standardkonfiguration von Debian und Ubuntu protokolliert nur error und höher, diese Zeilen erscheinen also nie. Heb die Stufe für die Fehlersuche an: Ergänze info in der Zeile error_log im http-Block, oder füg die Zeile dort hinzu, falls dein Paket error_log ganz oben in der Datei hat. Sie gilt für jede Website ohne eigenes error_log; setzt ein Server-Block eines, heb stattdessen dieses an:

/etc/nginx/nginx.conf · im http-Block
error_log /var/log/nginx/error.log info;
Debian · Ubuntu
sudo nginx -t && sudo systemctl reload nginx
sudo tail -f /var/log/nginx/error.log | grep -E 'client sent|plain HTTP'

Jede Ursache hat ihre eigene Zeile. So sehen sie im Log aus:

error.log · Stufe info
client sent too long header line: "Cookie: big=aaaaaaaa…" while reading client request headers
client sent plain HTTP request to HTTPS port while reading client request headers
client sent HTTP/1.1 request without "Host" header while reading client request headers
client sent duplicate host header: "Host: b", previous value: "Host: a"
client sent invalid host header while reading client request headers
client sent too long URI while reading client request line

info ist gesprächig: Es protokolliert auch jede geschlossene Keep-alive-Verbindung. Entferne das info wieder und lade neu, wenn du fertig bist. Die letzten 400er aus dem Access-Log listest du so auf; im Standardformat ist der Status das neunte Feld:

die letzten 400er
sudo awk '$9 == 400' /var/log/nginx/access.log | tail -n 20

Ursache 1: Request-Header oder Cookie zu groß

Das ist der 400, auf den ganz normale Besucher stoßen. nginx liest Request-Header in einen 1-KB-Puffer und, wenn der nicht reicht, in large_client_header_buffers: standardmäßig 4 Puffer zu je 8 KB. Eine einzelne Header-Zeile über 8 KB bekommt einen 400 mit Request Header Or Cookie Too Large, ebenso ein Satz Header, der mehr als vier Puffer braucht. Seit nginx 1.19.7 regelt dieselbe Direktive auch HTTP/2, ein Wechsel des Protokolls hilft also nicht.

Die üblichen Gründe, warum die Header wachsen:

  • Cookies für Analytics, A/B-Tests und Einwilligungen, die auf der übergeordneten Domain gesetzt werden, sodass jede Subdomain alle davon bekommt.
  • Eine Sitzung oder ein JWT im Cookie, das mit jeder Rolle oder Einstellung wächst, die hinzukommt.
  • Single-Sign-on-Tokens und in Firmennetzen große Kerberos-Header (Negotiate).
mit curl nachstellen
# ein 9-KB-Cookie: 400 Request Header Or Cookie Too Large
curl -s -o /dev/null -w '%{http_code}\n' \
  -H "Cookie: big=$(head -c 9000 /dev/zero | tr '\0' a)" https://example.com/

# Was schickt ein echter Browser? In den Entwicklertools des Browsers, Tab Netzwerk,
# Rechtsklick auf die fehlgeschlagene Anfrage, "Als cURL kopieren", dann den Teil -H 'cookie: ...' ansehen.

Um das Limit anzuheben, setz es im http-Block. Setzt du es nur in einem einzelnen server-Block, wendet nginx auf Verbindungen an dieser Adresse unter Umständen den Wert des Standardservers an, und die Änderung scheint nichts zu bewirken.

/etc/nginx/nginx.conf · im http-Block
large_client_header_buffers 4 16k;
anwenden und testen
sudo nginx -t && sudo systemctl reload nginx
curl -s -o /dev/null -w '%{http_code}\n' \
  -H "Cookie: big=$(head -c 9000 /dev/zero | tr '\0' a)" https://example.com/

Puffer werden pro Verbindung nur bei Bedarf belegt, 16k ist also günstig. Brauchst du deutlich mehr als 32k, reparier lieber die Cookies: eine Sitzungs-ID im Cookie, die Daten auf dem Server, und Cookies, die niemand liest, lässt du ablaufen.

Hinter einem Reverse Proxy gewinnt das kleinste Limit in der Kette. Heb nginx auf 32 KB an, und eine Node.js-App dahinter antwortet trotzdem bei 16 KiB mit 431. Die Standardwerte:

ServerStandardlimitAntwort bei ÜberschreitungEinstellung
nginx8 KB pro Header-Zeile, 4 Puffer400 (414 für die Anfragezeile)large_client_header_buffers
Apache 2.48.190 Byte pro Header-Feld, 100 Felder400LimitRequestFieldSize, LimitRequestFields
Node.js16 KiB für alle Header zusammen431--max-http-header-size
IIS (HTTP.sys)16 KB pro Header, 16 KB für Anfragezeile plus Header400MaxFieldLength, MaxRequestBytes in der Registry

Node startest du dafür mit node --max-http-header-size=32768 server.js. Unter Windows liegen die Werte von HTTP.sys unter HKLM\System\CurrentControlSet\Services\HTTP\Parameters und greifen nach einem Neustart des HTTP-Dienstes; Microsoft weist darauf hin, dass höhere Werte mehr Speicher und mehr Angriffsfläche bedeuten. Das umgekehrte Problem, ein Backend, dessen Antwort-Header für nginx zu groß sind, ist kein 400: Es zeigt sich als upstream sent too big header und als 502 Bad Gateway (engl.).

Ursache 2: „The plain HTTP request was sent to HTTPS port“

Ein Client hat reines HTTP mit einem Port gesprochen, der TLS erwartet. nginx antwortet mit 400 und genau diesem Satz. Woher das kommt:

  • Eine URL wie http://example.com:443/, von Hand eingetippt oder von einer App zusammengesetzt, die Schema und Port aneinanderklebt.
  • Ein Uptime-Monitor oder Health-Check, der auf HTTP an Port 443 eingestellt ist.
  • Ein Reverse Proxy mit proxy_pass http://…, der auf ein Backend zeigt, das nur HTTPS spricht. Ist das Backend nginx, wird sein 400 direkt an den Besucher durchgereicht; nimm stattdessen proxy_pass https://….
nachstellen
curl -si http://example.com:443/ | head -n 1
# HTTP/1.1 400 Bad Request

Für Leute, die das falsche Schema eintippen, mach aus dem Fehler eine Weiterleitung. Intern nennt nginx diesen Fall 497, und error_page kann ihn abfangen:

im Server-Block, der mit ssl lauscht
# auf dem Standardport 443
error_page 497 =301 https://$host$request_uri;

# auf einem anderen TLS-Port wie 8443
error_page 497 =301 https://$host:$server_port$request_uri;

Das Spiegelbild, HTTPS an einen Port mit reinem HTTP, scheitert stattdessen im Client: curl meldet wrong version number, und Browser zeigen ERR_SSL_PROTOCOL_ERROR. Auf dem Server hinterlässt es eine Zeile im Access-Log mit Status 400, deren Anfrage mit \x16\x03\x01 beginnt. Das ist ein TLS-Handshake. Die meisten davon stammen von Scannern und lassen sich ignorieren.

Ursache 3: eine fehlerhafte Anfrage

HTTP/1.1 verlangt genau einen gültigen Host-Header, und RFC 9112 verpflichtet Server, mit 400 zu antworten, wenn er fehlt, mehrfach vorkommt oder ungültig ist. Browser machen das nie falsch; selbst geschriebene Clients, Geräte-Firmware, kaputte Proxys und Scanner schon. curl kann beide Fälle nachstellen:

nachstellen
curl -si -H 'Host:' http://example.com/ | head -n 1             # Host entfernt: 400
curl -si -H 'Host: exa mple' http://example.com/ | head -n 1    # ungültiger Host: 400

Ursache 4: die Anwendung hat die Anfrage abgelehnt

Kommt der 400 mit einem JSON-Body oder der Fehlerseite deines Frameworks, hat nginx die Anfrage weitergereicht und die Anwendung hat sie abgelehnt. Der Body sagt meist, warum, lies ihn also mit curl -si. Die häufigen Fehler:

  • JSON als Formular geschickt. curl -d kennzeichnet den Body als application/x-www-form-urlencoded, und viele APIs können ihn dann nicht lesen. Nimm --json (ab curl 7.82.0, Debian 12 und Ubuntu 24.04 haben es also), das außerdem die Header Content-Type und Accept auf JSON setzt.
  • Die Shell-Quotierung hat das JSON kaputt gemacht. Pack das JSON in einfache Anführungszeichen, oder leg es in eine Datei und schick --json @body.json.
  • Die Anwendung akzeptiert den Host nicht, den sie sieht. Standardmäßig schickt nginx die Upstream-Adresse ($proxy_host, etwa 127.0.0.1:8000) als Host. Django antwortet darauf mit 400, wenn ALLOWED_HOSTS auf deine Domain gesetzt ist. Reich den echten Host mit proxy_set_header Host $host; weiter, wie in der Anleitung zum Reverse Proxy.
curl
# falsch: formularcodierter Body, die API kann ihn nicht lesen
curl -si -d '{"name":"web-1"}' https://api.example.com/servers

# richtig: JSON-Body mit gesetztem Content-Type und Accept
curl -si --json '{"name":"web-1"}' https://api.example.com/servers
curl -si --json @body.json https://api.example.com/servers

Steht in der Fußzeile der Fehlerseite cloudflare statt nginx, wurde die Anfrage schon am Edge von Cloudflare abgelehnt und hat deinen Server nie erreicht, deshalb sind deine Logs leer.

Wie halte ich 400-Fehler fern?

  • Halte Cookies klein: eine ID im Cookie, die Daten auf dem Server. Setz Analytics-Cookies wo möglich auf den exakten Host statt auf die übergeordnete Domain.
  • Setz large_client_header_buffers einmal im http-Block, und halte das Limit des Anwendungsservers mindestens genauso groß.
  • Leite um, statt zu scheitern: error_page 497 auf TLS-Ports und ein Server auf Port 80, der alles zu HTTPS schickt.
  • Reich vom Reverse Proxy immer proxy_set_header Host $host; weiter.
  • Lies bei jedem 400 einer API den Body, bevor du etwas änderst. Ein zu großer Upload ist ein anderer Fehler: 413 Request Entity Too Large (engl.).

Die Pfade und Standardwerte oben sind die des Standard-nginx-Pakets auf einem Debian VPS oder einem Ubuntu VPS, die Befehle funktionieren auf einem frisch installierten Template also genau so, wie sie hier stehen.

Häufige Fragen

Was bedeutet „400 Bad Request“ auf Deutsch?

„Bad Request“ heißt „fehlerhafte“ oder „ungültige Anfrage“. Der HTTP-Statuscode 400 sagt, dass der Server die Anfrage nicht bearbeitet, weil sie aus seiner Sicht kaputt, zu groß oder ungültig ist, zum Beispiel wegen eines übergroßen Cookies, eines fehlenden Host-Headers oder reinem HTTP an einem HTTPS-Port.

Wie behebe ich den Fehler 400 in Chrome?

Öffne die Seite in einem Inkognito-Fenster. Lädt sie dort, lösch die Cookies für genau diese Website über das Symbol für Website-Informationen links neben der Adressleiste und lade neu. Scheitert sie auch inkognito, liegt es am Link selbst oder am Server: Prüf die URL oder wende dich an den Betreiber der Website.

Ist 400 Bad Request ein Fehler des Clients oder des Servers?

Im Sinne von HTTP ist es ein Client-Fehler: Der Server sagt, die Anfrage selbst sei fehlerhaft, zu groß oder ungültig. Die Ursache kann trotzdem beim Server liegen, etwa bei einem Link, der http:// auf Port 443 schickt, oder bei einem Reverse Proxy, der der Anwendung einen Host übergibt, den sie nicht akzeptiert.