Коротко: ошибка 502 Bad Gateway значит, что шлюз или прокси (nginx, Apache, балансировщик нагрузки или CDN) получил от сервера за ним неверный ответ или не получил никакого. Сам прокси работает, не работает приложение за ним. Посетителю остаётся обновить страницу и подождать, а владелец сайта найдёт причину в логе ошибок прокси.
# 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, внизу nginx | nginx | Бэкенд не запущен, неверный сокет или порт, либо бэкенд закрыл соединение |
| 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 | Сервер в бэкенде не прислал ничего пригодного |
| Пустая страница со статусом 502 | Caddy | В его логе названа ошибка подключения, например connect: connection refused |
Bad Gateway | Traefik | Нет ответа от сервиса или контейнера |
HTTP Error 502.3 или 502.5 Process Failure | IIS (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 (англ.) | Сервер отклонил соединение: веб-сервер остановлен или порт закрыт |
| 522 | TCP-соединение с сервером не установилось за отведённое время |
| 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 upstream | nginx не может открыть сокет, или 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, а ниже каждая причина разобрана подробно, для любого прокси.
Проверьте бэкенд напрямую
Запрос в обход прокси делит проблему надвое. Обратитесь к бэкенду на самом сервере, по точному адресу из вашей конфигурации:
# 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 serverPHP-FPM говорит на FastCGI, а не на HTTP, поэтому curl его не проверит. Зато проверит cgi-fcgi из пакета libfcgi-bin, через ping-страницу FPM. Включите её в пуле строкой ping.path = /ping, перезапустите 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 (англ.):
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:
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:
# бэкенды за 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 Gateway | 503 Service Unavailable | 504 Gateway Timeout |
|---|---|---|---|
| Кто отправляет | Прокси или шлюз | Сам сервер или прокси | Прокси или шлюз |
| Что случилось | Ответ бэкенда неверный, оборван или его невозможно получить | Сервер пока отказывает: перегрузка, обслуживание, ограничение частоты запросов | Бэкенд принял запрос, но не ответил вовремя |
| Типичная строка в логе nginx | connect() failed (111) | limiting requests | upstream 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 секунд.