Решение проблем

Ошибка 400 Bad Request: что отклонил сервер и как это исправить

Ошибка 400 Bad Request (HTTP 400): сервер отклонил искажённый или слишком большой запрос. Как исправить раздутые cookie, HTTP на порт HTTPS и запросы к API.

Опубликовано ~8 мин чтения
На этой странице

Коротко: ошибка 400 Bad Request, она же код HTTP 400 («неверный запрос»), значит, что сервер отказался обрабатывать запрос, потому что в нём что-то искажено или слишком велико: раздутая cookie, обычный HTTP на порт HTTPS, отсутствующий заголовок Host или тело, которое приложение не смогло разобрать. В браузере обычно помогает удалить cookie одного этого сайта. На своём сервере прочитайте строку под «400 Bad Request» на странице ошибки, затем лог ошибок nginx на уровне info: он называет точную причину.

полный ответ, а не только статус
curl -si https://example.com/path | head -n 20
# <title> страницы ошибки nginx называет причину, например
# <title>400 Request Header Or Cookie Too Large</title>

Что значит сообщение под 400 Bad Request?

Для отдельных сбоев у nginx есть несколько внутренних кодов, и он отправляет их все клиенту как 400, а причину пишет второй строкой. Текст страницы и есть первая подсказка:

Сообщение на страницеЧто случилосьРешение
Request Header Or Cookie Too LargeОдна строка заголовка, обычно Cookie, больше 8 КБ, или все заголовки вместе не помещаются в буферы nginxПосетителю: удалить cookie сайта. Владельцу: увеличить large_client_header_buffers и уменьшить cookie
The plain HTTP request was sent to HTTPS porthttp:// ушёл на порт, который ждёт TLS, обычно 443Использовать https:// или перенаправлять через error_page 497
No required SSL certificate was sentСервер требует клиентский сертификат (ssl_verify_client), а его не прислалиУстановить клиентский сертификат или убрать требование
Просто 400 Bad Request от nginxИскажённый запрос: заголовка Host нет, он повторяется или неверен, сломана строка запросаПричину называет лог ошибок на уровне info
400 с телом в JSONВаше приложение отвергло ввод: неверный JSON, нет нужного поля, недопустимый HostПрочитать тело ответа: обычно в нём названо поле

Два соседа выглядят похоже. Строка запроса (метод плюс URL) длиннее одного буфера в 8 КБ получает вместо этого 414 Request-URI Too Large, а Node.js отвечает на слишком большие заголовки кодом 431 Request Header Fields Too Large.

Как исправить ошибку 400, если вы посетитель?

Откройте ту же страницу в приватном окне (режим инкогнито). Если там она загружается, виноваты cookie, которые ваше обычное окно отправляет этому сайту: их слишком много, или одна из них разрослась или повредилась. Удалите cookie одного этого сайта через значок сведений о сайте слева от адресной строки и обновите страницу. Очищать всю историю браузера не нужно.

Если и в приватном окне ошибка остаётся, посмотрите на URL: случайный %, дважды закодированная ссылка или http:// вместе с :443 дают 400. Причиной бывают и расширения браузера, которые добавляют заголовки. Если ничего из этого не помогло, проблема на сервере, и исправить её может только его владелец.

Где nginx пишет причину ошибки 400?

Журнал доступа показывает 400, но не причину. Причину nginx пишет в лог ошибок на уровне info, а стандартная конфигурация Debian и Ubuntu записывает только error и выше, поэтому этих строк не видно. На время отладки поднимите уровень: допишите info в строку error_log внутри блока http или добавьте туда эту строку, если ваш пакет держит error_log в начале файла. Она действует на все сайты без собственного error_log; если блок server задаёт свой, поднимайте уровень в нём:

/etc/nginx/nginx.conf · внутри блока http
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'

У каждой причины своя строка:

error.log · уровень 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 шумный: он записывает и каждое закрытое keep-alive-соединение. Закончив, уберите info и снова перечитайте конфигурацию. Последние 400 из журнала доступа, где в формате по умолчанию статус стоит девятым полем, выводит такая команда:

последние 400
sudo awk '$9 == 400' /var/log/nginx/access.log | tail -n 20

Причина 1: слишком большой заголовок или cookie

Это та 400, на которую натыкаются обычные посетители. nginx читает заголовки запроса в буфер на 1 КБ, а если его не хватает, в large_client_header_buffers: по умолчанию это 4 буфера по 8 КБ. Одна строка заголовка длиннее 8 КБ получает 400 с текстом Request Header Or Cookie Too Large, как и набор заголовков, которому нужно больше четырёх буферов. Начиная с nginx 1.19.7 та же директива управляет и HTTP/2, так что смена протокола не поможет.

Обычные причины, по которым заголовки растут:

  • Cookie аналитики, A/B-тестов и согласия на cookie, выставленные на родительский домен, так что каждый поддомен получает их все.
  • Сессия или JWT в cookie, которая растёт с каждой добавленной ролью или настройкой.
  • Токены единого входа (SSO), а в корпоративных сетях большие заголовки Kerberos (Negotiate).
воспроизводим через curl
# cookie на 9 КБ: 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/

# что отправляет настоящий браузер? В инструментах разработчика, вкладка «Сеть» (Network),
# правый клик по неудачному запросу, «Copy as cURL», смотрите часть -H 'cookie: ...'.

Чтобы поднять лимит, задайте его в блоке http. Если задать его только внутри одного блока server, nginx может применить к соединениям на этом адресе значение сервера по умолчанию, и изменение будто ничего не делает.

/etc/nginx/nginx.conf · внутри блока http
large_client_header_buffers 4 16k;
применяем и проверяем
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/

Буферы выделяются на соединение, только когда они нужны, поэтому 16k обходится дёшево. Если приходится уходить далеко за 32k, лучше исправьте cookie: держите в cookie идентификатор сессии, а данные на сервере, а cookie, которые никто не читает, удаляйте, выставив им истёкший срок.

За обратным прокси действует наименьший лимит в цепочке. Поднимите лимит nginx до 32 КБ, и приложение на Node.js за ним всё равно ответит 431 на 16 КиБ. Значения по умолчанию:

СерверЛимит по умолчаниюОтвет при превышенииНастройка
nginx8 КБ на строку заголовка, 4 буфера400 (414 для строки запроса)large_client_header_buffers
Apache 2.48190 байт на поле заголовка, 100 полей400LimitRequestFieldSize, LimitRequestFields
Node.js16 КиБ на все заголовки вместе431--max-http-header-size
IIS (HTTP.sys)16 КБ на заголовок, 16 КБ на строку запроса вместе с заголовками400MaxFieldLength, MaxRequestBytes в реестре

Node запускайте с node --max-http-header-size=32768 server.js. В Windows значения HTTP.sys лежат в HKLM\System\CurrentControlSet\Services\HTTP\Parameters и вступают в силу после перезапуска службы HTTP; Microsoft называет их увеличение компромиссом между расходом памяти и поверхностью атаки. Обратная проблема, когда бэкенд отдаёт слишком большие для nginx заголовки ответа, — это уже не 400: она выглядит как upstream sent too big header и 502 Bad Gateway.

Причина 2: The plain HTTP request was sent to HTTPS port

Клиент обратился по обычному HTTP к порту, который ждёт TLS. nginx отвечает 400 с этой фразой. Откуда это берётся:

  • URL вида http://example.com:443/, набранный вручную или собранный приложением, которое склеивает схему и порт.
  • Мониторинг доступности или проверка состояния (health check), настроенные на HTTP через порт 443.
  • Обратный прокси с proxy_pass http://…, который указывает на бэкенд, говорящий только по HTTPS. Если бэкенд работает на nginx, его 400 уходит прямо к посетителю; используйте proxy_pass https://….
воспроизводим
curl -si http://example.com:443/ | head -n 1
# HTTP/1.1 400 Bad Request

Для тех, кто ошибается в схеме, превратите ошибку в перенаправление. Внутри nginx этот случай называется 497, и error_page может его перехватить:

в блоке server, который слушает с ssl
# на стандартном порту 443
error_page 497 =301 https://$host$request_uri;

# на нестандартном TLS-порту, например 8443
error_page 497 =301 https://$host:$server_port$request_uri;

Зеркальный случай, HTTPS на обычный HTTP-порт, ломается уже в клиенте: curl сообщает wrong version number, а браузеры показывают ERR_SSL_PROTOCOL_ERROR. На сервере от него остаётся строка в журнале доступа со статусом 400, запрос в которой начинается с \x16\x03\x01. Это начало TLS-рукопожатия. Чаще всего такие строки оставляют сканеры, и на них можно не обращать внимания.

Причина 3: искажённый запрос

HTTP/1.1 требует ровно один корректный заголовок Host, и RFC 9112 обязывает серверы отвечать 400, если его нет, он повторяется или неверен. Браузеры здесь не ошибаются; ошибаются самописные клиенты, прошивки устройств, сломанные прокси и сканеры. curl воспроизводит оба случая:

воспроизводим
curl -si -H 'Host:' http://example.com/ | head -n 1             # Host убран: 400
curl -si -H 'Host: exa mple' http://example.com/ | head -n 1    # неверный Host: 400

Причина 4: запрос отвергло приложение

Если 400 приходит с телом в JSON или со страницей ошибки вашего фреймворка, nginx передал запрос дальше, а отказало приложение. Тело обычно объясняет причину, так что прочитайте его через curl -si. Частые ошибки:

  • JSON отправлен как форма. curl -d помечает тело как application/x-www-form-urlencoded, и многие API тогда не могут его разобрать. Используйте --json (есть в curl 7.82.0 и новее, то есть в Debian 12 и Ubuntu 24.04): он заодно выставляет заголовки Content-Type и Accept на JSON.
  • Кавычки в оболочке сломали JSON. Заключайте JSON в одинарные кавычки или держите его в файле и отправляйте --json @body.json.
  • Приложение не принимает Host, который видит. По умолчанию nginx передаёт в Host адрес upstream ($proxy_host, например 127.0.0.1:8000). Django, у которого в ALLOWED_HOSTS указан ваш домен, отвечает на это 400. Передавайте настоящий Host через proxy_set_header Host $host;, как в статье об обратном прокси (англ.).
curl
# неверно: тело закодировано как форма, API не может его разобрать
curl -si -d '{"name":"web-1"}' https://api.example.com/servers

# верно: тело в JSON, Content-Type и Accept выставлены
curl -si --json '{"name":"web-1"}' https://api.example.com/servers
curl -si --json @body.json https://api.example.com/servers

Если внизу страницы ошибки написано cloudflare, а не nginx, запрос отклонён на границе сети Cloudflare и до вашего сервера не дошёл. Поэтому ваши логи и пусты.

Как не допускать ошибок 400?

  • Держите cookie маленькими: в cookie идентификатор, данные на сервере. Где можно, ставьте cookie аналитики на точный хост, а не на родительский домен.
  • Задайте large_client_header_buffers один раз, в блоке http, и держите лимит сервера приложений не меньше.
  • Перенаправляйте вместо отказа: error_page 497 на TLS-портах и сервер на порту 80, который отправляет всё на HTTPS.
  • Всегда передавайте proxy_set_header Host $host; из обратного прокси.
  • Прежде чем что-то менять, читайте тело каждой 400 от API. Слишком большая загрузка даёт другую ошибку: 413 Request Entity Too Large (англ.).

Пути и значения по умолчанию выше относятся к стандартному пакету nginx на Debian VPS или Ubuntu VPS, так что на свежем шаблоне команды работают так, как написаны.

Частые вопросы

Что значит ошибка 400 Bad Request?

Код HTTP 400 значит, что сервер отказался обрабатывать запрос, потому что тот искажён или слишком велик: раздутая cookie, обычный HTTP на порт HTTPS, неверный заголовок Host или тело, которое приложение не смогло разобрать. Формально это ошибка клиента, но причина может быть и на сервере, например ссылка с http:// на порт 443 или обратный прокси, который передаёт приложению Host, которого оно не принимает.

Как исправить ошибку 400 в браузере?

Откройте страницу в режиме инкогнито. Если там она загружается, удалите cookie одного этого сайта через значок сведений о сайте слева от адресной строки и обновите страницу. Если ошибка остаётся и в режиме инкогнито, виновата сама ссылка или сервер: проверьте URL или сообщите о проблеме владельцу сайта.

Что значит «400 Request Header Or Cookie Too Large»?

nginx отклонил запрос, потому что одна строка заголовка, обычно Cookie, больше его буфера в 8 КБ или все заголовки вместе не помещаются в четыре таких буфера. Посетитель исправляет это удалением cookie сайта. Владелец сервера может задать в блоке http директиву «large_client_header_buffers 4 16k;» и перечитать конфигурацию nginx, но стоит и выяснить, почему cookie разрослись.

Читайте также

Все статьи раздела

Ошибка 401 Unauthorized: что требует сервер и почему не доходят учётные данные

Ошибка 401 Unauthorized (HTTP 401): сервер ждёт действительные учётные данные и не получил их. Как прочитать WWW-Authenticate и починить auth_basic и токены.

ERR_SSL_PROTOCOL_ERROR («Этот сайт не может обеспечить безопасное подключение»): причины и решение

«Этот сайт не может обеспечить безопасное подключение» (ERR_SSL_PROTOCOL_ERROR) в Chrome и Яндекс Браузере: что проверить у себя и как исправить на сервере.

ERR_CONNECTION_RESET: кто сбросил соединение и что делать

ERR_CONNECTION_RESET: соединение оборвал пакет TCP reset. Если не открывается ни один сайт, проверьте VPN, прокси, антивирус; если один — его сервер и файрвол.

Ошибка 429 Too Many Requests: что значит и как исправить в nginx, API и ботах

Ошибка 429 (HTTP 429 Too Many Requests) значит, что сработал лимит запросов. Как найти, чей он, настроить nginx limit_req и научить скрипт ждать Retry-After.

Ошибка 502 Bad Gateway: что это значит и как её исправить

Ошибка 502 Bad Gateway: прокси получил от приложения за ним неверный ответ или никакого. Что это значит, кто её показывает, что пишет лог и как исправить.

502 Bad Gateway в nginx: виноват бэкенд, а не nginx

502 Bad Gateway значит, что nginx не получил ответа от вашего приложения. Читаем error.log и устраняем четыре причины: бэкенд не запущен, сокет, таймаут, права.