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

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

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

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

Коротко: ошибка 502 Bad Gateway значит, что шлюз или прокси (nginx, Apache, балансировщик нагрузки или CDN) получил от сервера за ним неверный ответ или не получил никакого. Сам прокси работает, не работает приложение за ним. Посетителю остаётся обновить страницу и подождать, а владелец сайта найдёт причину в логе ошибок прокси.

Debian · Ubuntu · на сервере, сразу после 502
# 1. Что увидел прокси?
sudo tail -n 20 /var/log/nginx/error.log          # Apache: /var/log/apache2/error.log

# 2. Запущен ли бэкенд и слушает ли он там, где его ждёт прокси?
sudo systemctl status php8.3-fpm --no-pager       # или служба вашего приложения
sudo ss -tlnp | grep ':3000'                      # TCP-бэкенды
sudo ss -xlp | grep -E 'php|gunicorn|sock'        # Unix-сокеты

# 3. Спросить бэкенд напрямую, без прокси
curl -sS -o /dev/null -w '%{http_code} in %{time_total}s\n' http://127.0.0.1:3000/

Замените php8.3-fpm на свою версию PHP (её покажет ls /run/php/), а 3000 на порт из вашего proxy_pass. Если бэкенд отвечает напрямую, но не через прокси, проблема в адресе, сокете или правах доступа между ними.

Что значит ошибка 502 Bad Gateway?

RFC 9110 определяет 502 так: сервер, «действуя как шлюз или прокси, получил недопустимый ответ от вышестоящего сервера, к которому обратился, пытаясь выполнить запрос». На практике большинство прокси отвечают 502 и тогда, когда ответа не было вовсе: соединение отклонено, сокета не существует или бэкенд закрыл соединение, так и не ответив.

Дословно Bad Gateway переводится как «плохой шлюз». Точнее смысл передаёт «шлюз получил негодный ответ»: плох не шлюз, а ответ, который он получил от вышестоящего сервера.

Главное здесь в том, кто отправляет эту ошибку. 502 пишет прокси, стоящий впереди, о сервере за ним. Упавшее приложение само 502 не отправляет: оно просто перестаёт отвечать, а сообщают об этом nginx или балансировщик. (Приложение, которое обращается к другому сервису, может передать дальше собственную 502, и тогда об этом говорит его лог.) Браузер показывает страницу прокси, поэтому одна и та же ошибка выглядит как 502 Bad Gateway, HTTP Error 502, 502 Proxy Error или фирменная страница ошибки, в зависимости от того, что стоит впереди.

Кто показывает 502?

Что вы видитеКто отправилЧто это обычно значит
Белая страница, 502 Bad Gateway, внизу nginxnginxБэкенд не запущен, неверный сокет или порт, либо бэкенд закрыл соединение
Proxy Error. The proxy server received an invalid response from an upstream server.Apache mod_proxyОтвет бэкенда оборвался или был не HTTP (AH01102)
502 Bad Gateway. The server returned an invalid or incomplete response.HAProxyСервер в бэкенде не прислал ничего пригодного
Пустая страница со статусом 502CaddyВ его логе названа ошибка подключения, например connect: connection refused
Bad GatewayTraefikНет ответа от сервиса или контейнера
HTTP Error 502.3 или 502.5 Process FailureIIS (ARR или модуль ASP.NET Core)Бэкенд упал, не ответил вовремя или приложение не запустилось
Страница 502 в оформлении CloudflareСобственная сеть CloudflareПроблема внутри Cloudflare; проверьте его страницу статуса
Обычная страница 502, хотя сайт работает за CloudflareПрокси на вашем сервере; Cloudflare передал страницу как естьПрименимо всё, что написано в этой статье

Apache и HAProxy здесь ведут себя иначе, чем nginx: если к бэкенду вообще не удаётся подключиться, они отвечают кодом 503 Service Unavailable (англ.), а в логе Apache это запись AH01114. Код 502 они оставляют для бэкенда, который ответил чем-то сломанным. nginx и Caddy используют 502 в обоих случаях.

Cloudflare: собственная 502 или одна из ошибок с 520 по 526

Когда сам Cloudflare не может получить пригодный ответ от вашего сервера, он вместо 502 использует собственные коды. Согласно документации Cloudflare, 502 или 504 в оформлении Cloudflare указывают на его собственную сеть, а обычная страница без оформления пришла с вашего сервера и была передана насквозь. Для кодов, которые касаются вашего сервера (origin), есть отдельные статьи:

КодЧто увидел Cloudflare
520 (англ.)Сервер ответил чем-то пустым, сброшенным или искажённым
521 (англ.)Сервер отклонил соединение: веб-сервер остановлен или порт закрыт
522TCP-соединение с сервером не установилось за отведённое время
523 (англ.)Сервер недостижим: неверный IP, маршрутизация или DNS
524 (англ.)Соединение есть, но HTTP-ответ не пришёл вовремя
525 (англ.)Не удалось TLS-рукопожатие с сервером
526 (англ.)Сертификат сервера недействителен в режиме Full (strict)

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

  • Обновите страницу через минуту. Многие 502 длятся, только пока приложение перезапускается или выкатывается новая версия. Обновление каждую секунду добавляет нагрузки и не помогает.
  • Проверьте другие сайты. Если 502 показывают все сайты, виноват шлюз на вашей стороне: корпоративный прокси, VPN или защитное ПО, которое проверяет трафик. Попробуйте без него.
  • Не тратьте время на cookie и кеш. 502 возникает между двумя серверами, поэтому их очистка редко что-то меняет. Исключение составляет сайт, который только что переехал на другой сервер: тут очистка кеша DNS приведёт вас на новый адрес.
  • Если ошибка не проходит, сообщите владельцу. Запишите время и URL. Этого хватит, чтобы он нашёл нужную строку в своём логе.

Владельцу сайта: что пишет лог ошибок

Прокси пишет по строке на каждый неудачный запрос, и формулировка называет причину. Вот сообщения nginx, которые на VPS встречаются чаще всего. nginx их не переводит, так что текст одинаков на любом сервере:

Сообщение в логеПричинаРешение
connect() failed (111: Connection refused) while connecting to upstreamНа этом адресе и порту никто не слушает: приложение остановлено, упало или слушает в другом местеЗапустить, проверить порт
connect() to unix:/run/php/php8.3-fpm.sock failed (2: No such file or directory)Неверный путь к сокету, часто после обновления PHP, или FPM остановленИсправить путь
(13: Permission denied) while connecting to upstreamnginx не может открыть сокет, или SELinux блокирует соединениеВладелец сокета или переключатель SELinux
upstream prematurely closed connection while reading response header from upstreamБэкенд упал или прервал запрос, не успев ответитьПадение, OOM killer или таймаут воркера
recv() failed (104: Connection reset by peer) while reading response header from upstreamТо же семейство: соединение сброшено посреди запросаСмотреть лог бэкенда
upstream sent too big header while reading response header from upstreamЗаголовки ответа (обычно cookie) не помещаются в буфер проксиУвеличить размер буфера
no live upstreams while connecting to upstreamВсе серверы в блоке upstream помечены как отказавшиеПочинить бэкенды; nginx снова попробует их после fail_timeout

С PHP-FPM случай закрытого соединения выглядит как upstream prematurely closed FastCGI stdout. У Apache аналог такой: AH01102: error reading status line from remote server. В статье о 502 в nginx собран короткий чек-лист только для nginx, а ниже каждая причина разобрана подробно, для любого прокси.

Проверьте бэкенд напрямую

Запрос в обход прокси делит проблему надвое. Обратитесь к бэкенду на самом сервере, по точному адресу из вашей конфигурации:

на сервере · HTTP-бэкенды
# TCP: Node, gunicorn, uvicorn, другой веб-сервер
curl -sS -i http://127.0.0.1:3000/ | head -n 5

# Unix-сокет, например gunicorn на /run/gunicorn.sock
curl -sS -i --unix-socket /run/gunicorn.sock http://localhost/ | head -n 5

Если файла сокета нет, curl 7.88.1 в Debian 12 выводит сбивающую с толку строку: он называет порт 80, хотя в сеть вообще не обращался:

Вывод, если сокета нет
curl: (7) Failed to connect to localhost port 80 after 0 ms: Couldn't connect to server

PHP-FPM говорит на FastCGI, а не на HTTP, поэтому curl его не проверит. Зато проверит cgi-fcgi из пакета libfcgi-bin, через ping-страницу FPM. Включите её в пуле строкой ping.path = /ping, перезапустите FPM, затем:

Debian · Ubuntu · проверка 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
# несколько строк заголовков, затем: pong

Если здесь ответ есть, а через nginx приходит 502, бэкенд в порядке, а сломана связь между ними. Если ответа нет и здесь, прокси жалуется по делу.

Причина 1: бэкенд не запущен

Connection refused значит, что на этом адресе никто не слушает. Посмотрите на службу и её собственный лог, который читает journalctl (англ.):

Debian · Ubuntu · состояние службы и последние сообщения
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         # сколько уже было автоматических перезапусков

Active: activating (auto-restart) вместе с status=1/FAILURE означает цикл падений: systemd перезапускает приложение, оно падает при старте, и каждый запрос в промежутке получает 502. Причину называет журнал. Обычно это недостающая переменная окружения, синтаксическая ошибка после деплоя, недоступная база данных или уже занятый порт; этот случай разобран в статье address already in use (англ.). Устраните причину, затем выполните sudo systemctl restart myapp.

Причина 2: прокси указывает на неверный адрес или сокет

  • Обновление PHP сменило сокет. php8.2-fpm.sock превращается в php8.3-fpm.sock, а в fastcgi_pass по-прежнему старое имя. ls /run/php/ покажет, что существует на самом деле.
  • localhost не один адрес. proxy_pass http://localhost:3000 разрешается и в ::1, и в 127.0.0.1, и nginx пробует второй адрес, когда первый отказывает, поэтому лог заполняется отказами на адресе, которым приложение не пользуется. Настоящая 502 появляется, когда стороны называют разные адреса: nginx проксирует на 127.0.0.1, а приложение на Node (версии 17 и новее), привязанное к localhost, слушает только ::1. Поле upstream: в строке лога показывает, какой адрес не ответил. Указывайте один и тот же явный адрес с обеих сторон.
  • Контейнер получил новый IP. Проксирование на внутренний IP контейнера ломается после его перезапуска. Опубликуйте порт на 127.0.0.1 и проксируйте туда или поместите прокси в ту же сеть Docker и обращайтесь по имени сервиса.
сравните, куда смотрит прокси и кто слушает
sudo nginx -T 2>/dev/null | grep -nE 'proxy_pass|fastcgi_pass|server .*:'
sudo ss -tlnp
ls -l /run/php/

Причина 3: права на сокет и SELinux

(13: Permission denied) на Unix-сокете значит, что пользователь воркера nginx не может его открыть. Пул PHP-FPM в Debian создаёт сокет для www-data с режимом 0660, и это подходит nginx из Debian. Пакеты с nginx.org запускают nginx от пользователя nginx, и тогда сокет для него закрыт. Приведите пул в соответствие с пользователем, от которого работает nginx:

/etc/php/8.3/fpm/pool.d/www.conf
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = nginx        ; группа пользователя воркера nginx
listen.mode = 0660

После этого перезапустите FPM. В Rocky, AlmaLinux и RHEL та же ошибка для TCP-бэкенда вроде 127.0.0.1:3000 обычно идёт от SELinux: он не даёт веб-серверу открывать сетевые соединения, пока вы не разрешите это командой sudo setsebool -P httpd_can_network_connect 1. Сначала убедитесь в отказе через sudo ausearch -m avc -ts recent.

Причина 4: бэкенд упал или был убит посреди запроса

prematurely closed и Connection reset by peer значат, что бэкенд принял запрос и пропал. Так бывает в трёх случаях:

  • OOM killer ядра. Его видно по sudo journalctl -k | grep -i 'killed process'. PHP-FPM записывает потерю воркера как child 12345 exited on signal 9 (SIGKILL). Найти, что съело память, поможет статья о процессах, убитых OOM killer (англ.).
  • Падение. exited on signal 11 (SIGSEGV) в логе FPM указывает на расширение PHP; необработанное исключение в Node завершает процесс, если его никто не перехватил, и systemd поднимает его мгновение спустя.
  • Собственный таймаут бэкенда. gunicorn по умолчанию убивает воркер через 30 секунд, а PHP-FPM по request_terminate_timeout пишет в лог execution timed out ... terminating. nginx видит закрытое соединение и отвечает 502, а не 504. Это медленный запрос, и он разобран в статье nginx 504 Gateway Timeout.

Причина 5: слишком большие заголовки ответа

nginx читает заголовки ответа бэкенда в один буфер, по умолчанию 4 или 8 КБ (одна страница памяти). Вход в систему, который ставит несколько больших cookie, или фреймворк, отправляющий длинный заголовок Content-Security-Policy или Link, переполняет его, и в логе появляется upstream sent too big header. Увеличивайте размер буфера и сами буферы вместе, иначе nginx -t завершится ошибкой про proxy_busy_buffers_size:

nginx · блок server или location
# бэкенды за proxy_pass
proxy_buffer_size 16k;
proxy_buffers 8 16k;

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

Затем sudo nginx -t && sudo systemctl reload nginx. Если заголовки продолжают расти, выясните почему: cookie, которая растёт с каждым визитом, — это баг, а Cloudflare отвергает заголовки ответа сверх собственного лимита ошибкой 520.

Причина 6: файрвол или keep-alive между прокси и бэкендом

  • Бэкенд на другом сервере. Файрвол, который отклоняет соединение, даёт (111: Connection refused) или (113: No route to host) и 502; файрвол, который отбрасывает пакеты, даёт таймаут и 504. Разрешите адрес прокси на порту бэкенда, например через ufw (англ.), и проверьте с прокси командой curl -v --connect-timeout 5 http://10.0.0.5:8080/.
  • Редкие случайные 502 за балансировщиком. Когда бэкенд закрывает простаивающие keep-alive-соединения раньше, чем балансировщик перестаёт их переиспользовать, запрос иногда попадает в соединение, которое как раз закрывается. server.keepAliveTimeout в Node по умолчанию равен 5 секундам, это меньше таймаута простоя у большинства балансировщиков. Сделайте keep-alive бэкенда длиннее, чем у балансировщика.

Чем отличаются 502, 503 и 504

Вопрос502 Bad Gateway503 Service Unavailable504 Gateway Timeout
Кто отправляетПрокси или шлюзСам сервер или проксиПрокси или шлюз
Что случилосьОтвет бэкенда неверный, оборван или его невозможно получитьСервер пока отказывает: перегрузка, обслуживание, ограничение частоты запросовБэкенд принял запрос, но не ответил вовремя
Типичная строка в логе nginxconnect() failed (111)limiting requestsupstream timed out (110)
Заголовок Retry-AfterНетЧасто, при плановом обслуживанииНет
Что проверить первымЗапущен ли бэкенд и слушает ли он там, где его ждёт прокси?Пул воркеров, флаг обслуживания, лимиты запросовМедленные запросы, медленные внешние вызовы, таймауты

Для 503 Service Unavailable (англ.) и 504 Gateway Timeout есть отдельные статьи. Ещё один сосед, 500 Internal Server Error (англ.), означает, что бэкенд ответил, но собственной ошибкой.

Когда бэкенд снова отвечает, проверка HTTP-заголовков (англ.) запросит страницу извне вашей сети и покажет код статуса и заголовки, которые получают все остальные.

Как не допускать 502 на своём сервере

  • Пусть systemd перезапускает упавшее приложение. Restart=on-failure в юните превращает падение в несколько секунд 502 вместо простоя; сам юнит показан в статье о службах systemd (англ.).
  • Перечитывайте конфигурацию nginx, а не перезапускайте его. systemctl reload nginx запускает новые воркеры и даёт старым закончить свои запросы, а restart их обрывает.
  • Оставляйте запас памяти. Подбирайте pm.max_children и число воркеров приложения под имеющуюся RAM и добавьте swap (англ.) как буфер, чтобы час пик не заканчивался OOM killer. Если нагрузка просто переросла сервер, поможет тариф Standard KVM с большим объёмом памяти.
  • Следите за сайтом снаружи. Монитор вроде Uptime Kuma (англ.) заметит 502 раньше, чем вам напишут посетители.

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

Что значит ошибка 502 Bad Gateway?

Сервер, который работает шлюзом или прокси (nginx, Apache, балансировщик нагрузки или CDN), получил от сервера за ним неверный ответ или не получил никакого. Сам прокси работает, а приложение или сервер, которому он передаёт запросы, остановлен, недоступен или оборвал ответ. Дословно Bad Gateway переводится как «плохой шлюз».

Как исправить ошибку 502?

Посетителю: подождите минуту и обновите страницу один раз. Очистка cookie и кеша браузера почти никогда не помогает, потому что ошибка возникает между двумя серверами; если 502 показывают все сайты, виноваты VPN, корпоративный прокси или защитное ПО на вашей стороне. Владельцу сайта: откройте лог ошибок прокси, например /var/log/nginx/error.log. Connection refused значит, что бэкенд не запущен, No such file or directory у файла .sock указывает на неверный путь к сокету, Permission denied — на права или SELinux, prematurely closed — на падение бэкенда посреди запроса.

Чем ошибка 502 отличается от 504?

Обе приходят от прокси. 502 значит, что ответ бэкенда был неверным или его вообще не удалось получить, например соединение отклонено или бэкенд упал посреди запроса. 504 значит, что бэкенд принял запрос, но не ответил до истечения таймаута прокси, в nginx это по умолчанию 60 секунд.

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

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

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

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

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

Ошибка 504 Gateway Timeout (HTTP 504): прокси ждал ответа от сервера за ним и сдался. Что это значит, какой таймаут сработал и как ускорить медленную часть.

504 Gateway Time-out в nginx: как найти медленный бэкенд и исправить ошибку

504 значит, что nginx ждал ответа от PHP-FPM, Node или другого бэкенда и не дождался. Читаем лог, увеличиваем нужный таймаут и чиним медленный запрос.

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

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

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

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