502 bad gateway nginx — понятное руководство по поиску причины и исправлению ошибки

Ошибка 502 в Nginx почти всегда указывает на сбой связи между Nginx и сервисом за ним: PHP-FPM, Node.js, Python-приложением, контейнером, Apache или другим upstream. Быстрое исправление начинается с логов, проверки backend-сервиса, сокета или порта, затем переходит к конфигурации, правам, ресурсам и таймаутам.

502 bad gateway nginx
502 bad gateway nginx

502 возникает на границе между Nginx и backend-сервисом

Код 502 Bad Gateway относится к классу серверных HTTP-ошибок. В спецификации HTTP он означает, что сервер в роли шлюза или прокси получил недействительный ответ от входящего сервера, к которому обратился для выполнения запроса. Для сайта на Nginx это обычно выглядит так: браузер отправляет запрос, Nginx принимает его, передаёт дальше в приложение, затем возвращает пользователю страницу с ошибкой, если корректный ответ от backend-сервиса получить не удалось.

В реальной инфраструктуре Nginx часто работает как reverse proxy. Он принимает внешний трафик, обслуживает статику, завершает TLS-соединение, направляет запросы в приложение и может балансировать нагрузку между несколькими backend-серверами. Официальная документация NGINX описывает передачу запросов через proxy_pass для HTTP-сервисов и через fastcgi_pass для FastCGI, включая PHP-FPM.

Упрощённая цепочка выглядит так:

Браузер → Nginx → PHP-FPM / Node.js / Python / Docker-контейнер / Apache → приложение → база данных

Если приложение падает, PHP-FPM слушает другой сокет, контейнер перезапускается, порт закрыт файрволом или backend отдаёт повреждённые заголовки, Nginx показывает 502. Сам Nginx в этот момент может работать исправно: проблема часто находится на следующем участке цепочки.

Первые минуты диагностики дают больше пользы, чем хаотичная правка конфига

Главная ошибка при 502 — сразу менять таймауты, буферы и случайные параметры. Такой подход маскирует причину и может создать новые сбои. Надёжнее идти от факта к гипотезе: сначала зафиксировать симптом, затем найти строку в логах, после этого проверять конкретный сервис.

Начните с проверки статуса Nginx:

sudo systemctl status nginx
sudo nginx -t

Команда nginx -t проверяет синтаксис конфигурации. Если в конфиге опечатка, неверный блок или сломанный include-файл, Nginx сообщит об этом до перезагрузки.

Дальше откройте error log:

sudo tail -n 100 /var/log/nginx/error.log

Для конкретного сайта путь может быть другим:

sudo tail -n 100 /var/log/nginx/example.com.error.log

Access log помогает увидеть, какие URL дают 502 и с каких IP идут запросы:

sudo tail -n 100 /var/log/nginx/access.log

Модуль логирования Nginx пишет логи запросов в заданном формате через access_log, а сообщения об ошибках помогают связать код ответа с upstream, сокетом, IP, портом и фазой обработки запроса.

Полезные фразы в error log:

Фрагмент в логеВероятная причинаПервый шаг
connect() failed (111: Connection refused)Backend-сервис не слушает порт или упалПроверить systemctl status, порт и контейнер
No such file or directory рядом с .sockNginx смотрит на неверный Unix-сокетСверить fastcgi_pass и фактический сокет PHP-FPM
Permission deniedNginx не имеет доступа к сокету или файлуПроверить владельца, группу и права
upstream timed outBackend отвечает слишком долгоСмотреть нагрузку, медленные запросы, БД, таймауты
upstream sent too big headerЗаголовки ответа превышают буферПроверить cookies, headers, буферы
upstream prematurely closed connectionBackend закрыл соединение до полного ответаСмотреть логи приложения и PHP-FPM

PHP-FPM чаще всего ломает связку Nginx с PHP-сайтом

Для WordPress, Laravel, Symfony, Drupal, Bitrix и других PHP-проектов Nginx обычно передаёт PHP-запросы в PHP-FPM через Unix-сокет или TCP-порт. Официальная документация Nginx показывает, что fastcgi_pass может указывать на localhost:9000 или на Unix-сокет вида unix:/tmp/fastcgi.socket.

Сначала проверьте службу PHP-FPM:

sudo systemctl status php8.3-fpm

Версия может отличаться:

systemctl list-units --type=service | grep fpm

Если служба остановлена, запустите её:

sudo systemctl restart php8.3-fpm

Проверьте, какой сокет реально создан:

ls -lah /run/php/

В конфигурации Nginx путь должен совпадать:

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

Типичная причина 502 после обновления PHP — старый путь в fastcgi_pass. Например, на сервере уже работает php8.3-fpm.sock, а в конфиге остался php8.2-fpm.sock.

После изменения конфигурации выполните проверку и мягкую перезагрузку:

sudo nginx -t
sudo systemctl reload nginx

Затем проверьте логи PHP-FPM:

sudo journalctl -u php8.3-fpm -n 100 --no-pager
sudo tail -n 100 /var/log/php8.3-fpm.log

На разных дистрибутивах путь к логам отличается. Если отдельного файла нет, используйте journalctl.

Reverse proxy требует точного адреса upstream

Для Node.js, Python, Go, Java и микросервисов Nginx часто проксирует запросы через proxy_pass:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Если приложение слушает порт 3001, а Nginx отправляет запросы на 3000, появится 502. Если приложение слушает только внутри Docker-сети, а Nginx расположен на хосте, адрес 127.0.0.1 может указывать на сам Nginx, а не на контейнер.

Проверьте порт:

sudo ss -ltnp | grep -E ':3000|:8000|:8080'

Проверьте локальный ответ backend-сервиса с сервера:

curl -I http://127.0.0.1:3000
curl http://127.0.0.1:3000/health

Если Nginx проксирует в upstream-группу, убедитесь, что все адреса доступны:

upstream app_backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

server {
    location / {
        proxy_pass http://app_backend;
    }
}

Документация NGINX указывает, что upstream-модуль может возвращать 502, когда сервер для обработки запроса выбрать нельзя в пределах заданного условия очереди или таймаута.

Docker и контейнеры добавляют сетевой слой к диагностике

В Docker-схеме 502 часто возникает из-за неправильного имени сервиса, закрытого порта, несовпадения сети или перезапуска контейнера. Проверка начинается с состояния контейнеров:

docker ps
docker compose ps

Логи приложения:

docker logs --tail=100 app
docker compose logs --tail=100 app

Проверка из контейнера Nginx:

docker compose exec nginx sh
curl -I http://app:3000

Если Nginx находится в одном docker-compose.yml с приложением, в proxy_pass обычно используют имя сервиса:

location / {
    proxy_pass http://app:3000;
}

Если Nginx установлен на хосте, а приложение работает в контейнере, порт контейнера должен быть опубликован наружу:

services:
  app:
    ports:
      - "3000:3000"

После правки Docker-конфигурации проверьте доступ с хоста:

curl -I http://127.0.0.1:3000

Права на сокет и владельцы процессов часто дают Permission denied

Unix-сокет PHP-FPM быстрее и удобнее TCP-порта на одном сервере, хотя требует корректных прав доступа. Если в error log есть Permission denied рядом с .sock, Nginx видит сокет, но не может им пользоваться.

Проверьте пользователя Nginx:

ps aux | grep nginx

Проверьте настройки пула PHP-FPM:

sudo grep -E '^(user|group|listen|listen.owner|listen.group|listen.mode)' /etc/php/8.3/fpm/pool.d/www.conf

Рабочий вариант для Ubuntu часто выглядит так:

user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

После изменения пула:

sudo systemctl restart php8.3-fpm
sudo systemctl reload nginx

Если Nginx работает от другого пользователя, например nginx, добавьте согласованные права через группу или измените настройки пула осознанно. Открывать сокет режимом 0777 ради быстрого запуска сайта опасно: такой режим расширяет доступ к backend-интерфейсу.

Таймауты помогают только после проверки медленного backend

Таймауты стоит менять, когда логи показывают upstream timed out, а приложение действительно выполняет долгую задачу: экспорт, генерацию отчёта, тяжёлый запрос к базе, обращение к внешнему API. Для FastCGI Nginx использует fastcgi_connect_timeout, fastcgi_send_timeout, fastcgi_read_timeout; документация указывает, что fastcgi_read_timeout задаёт интервал между двумя операциями чтения ответа, а значение по умолчанию равно 60 секундам.

Пример для PHP-FPM:

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;

    fastcgi_connect_timeout 60s;
    fastcgi_send_timeout 120s;
    fastcgi_read_timeout 120s;
}

Для HTTP reverse proxy используются свои директивы:

location / {
    proxy_pass http://127.0.0.1:3000;

    proxy_connect_timeout 60s;
    proxy_send_timeout 120s;
    proxy_read_timeout 120s;
}

Увеличение таймаутов не лечит перегрузку приложения. Если PHP-воркеры заняты, база отвечает медленно, очередь задач выполняется в веб-запросе или внешний API зависает, пользователь всё равно будет ждать слишком долго. В такой ситуации лучше переносить тяжёлые операции в фоновые задачи, оптимизировать запросы к базе, включать кеширование и ограничивать параллельные тяжёлые операции.

Большие заголовки ответа приводят к upstream sent too big header

Ошибка upstream sent too big header while reading response header from upstream часто связана с крупными cookies, слишком большими заголовками авторизации, раздутыми session-cookie или большим количеством служебных заголовков. В PHP-проектах это может проявляться после установки плагина, изменения механизма авторизации или перехода на SSO.

Для FastCGI можно увеличить буферы:

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;

    fastcgi_buffer_size 32k;
    fastcgi_buffers 16 16k;
}

Для reverse proxy:

location / {
    proxy_pass http://127.0.0.1:3000;

    proxy_buffer_size 32k;
    proxy_buffers 16 16k;
}

Официальная документация Nginx описывает fastcgi_buffer_size, fastcgi_buffers, proxy_buffer_size и proxy_buffers как параметры чтения ответа от upstream-сервера; первая часть ответа обычно содержит заголовки.

Увеличивать буферы стоит аккуратно. Если cookie разрослась до десятков килобайт, полезно найти источник роста и уменьшить объём данных в заголовках.

Нагрузка на сервер превращает единичную ошибку в массовый сбой

502 может появляться короткими всплесками во время пикового трафика. В такой ситуации backend перестаёт обслуживать новые запросы, воркеры заняты, очередь подключений растёт, а Nginx начинает получать отказы соединения, пустые ответы или обрывы.

Проверьте ресурсы:

top
htop
free -m
df -h
iostat -xz 1

Проверьте количество процессов PHP-FPM:

ps -ylC php-fpm8.3 --sort:rss

Проверьте настройки пула:

sudo grep -E '^(pm|pm.max_children|pm.start_servers|pm.min_spare_servers|pm.max_spare_servers|pm.max_requests)' /etc/php/8.3/fpm/pool.d/www.conf

Если в логах PHP-FPM есть сообщения о достижении pm.max_children, пулу не хватает процессов для текущей нагрузки. Увеличение pm.max_children требует расчёта по памяти: один PHP-процесс может занимать десятки или сотни мегабайт в зависимости от приложения. При недостатке RAM сервер начнёт активно использовать swap, и 502 станет чаще.

Простой расчёт:

доступная RAM для PHP-FPM / средний размер одного PHP-процесса = безопасный pm.max_children

Например, если под PHP-FPM доступно около 2 ГБ, а один процесс занимает 100 МБ, безопасное значение будет около 20 процессов с запасом для Nginx, базы данных, Redis и системных служб.

Конфигурация Nginx должна проходить проверку перед каждым reload

После любой правки конфигурации используйте:

sudo nginx -t

Затем применяйте изменения:

sudo systemctl reload nginx

Для аварийного перезапуска:

sudo systemctl restart nginx

reload мягче, потому что Nginx перечитывает конфигурацию без полного ручного остановa сервиса. restart полезен при зависшем состоянии, смене модулей или нестандартной проблеме, но на боевом сервере его лучше применять осознанно.

Минимальный порядок безопасной правки:

  1. Скопируйте текущий конфиг.
  2. Измените только один логический блок.
  3. Запустите nginx -t.
  4. Примените reload.
  5. Проверьте страницу через curl.
  6. Посмотрите error log.
  7. Зафиксируйте результат.
sudo cp /etc/nginx/sites-available/example.com /etc/nginx/sites-available/example.com.bak.$(date +%F-%H%M)
sudo nginx -t
sudo systemctl reload nginx
curl -I https://example.com

Практический сценарий для Laravel, WordPress и других PHP-проектов

Для сайта на PHP используйте такой маршрут диагностики:

# 1. Проверить Nginx
sudo nginx -t
sudo systemctl status nginx

# 2. Проверить PHP-FPM
sudo systemctl status php8.3-fpm
systemctl list-units --type=service | grep fpm

# 3. Проверить сокет
ls -lah /run/php/

# 4. Найти fastcgi_pass
sudo grep -R "fastcgi_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d

# 5. Сравнить путь сокета в Nginx с фактическим сокетом
# пример: unix:/run/php/php8.3-fpm.sock

# 6. Посмотреть логи
sudo tail -n 100 /var/log/nginx/error.log
sudo journalctl -u php8.3-fpm -n 100 --no-pager

# 7. Перезапустить PHP-FPM после исправления
sudo systemctl restart php8.3-fpm
sudo nginx -t
sudo systemctl reload nginx

Для Laravel дополнительно проверьте права на каталоги:

sudo chown -R www-data:www-data storage bootstrap/cache
sudo find storage bootstrap/cache -type d -exec chmod 775 {} \;
sudo find storage bootstrap/cache -type f -exec chmod 664 {} \;

Проверьте логи приложения:

tail -n 100 storage/logs/laravel.log

Если ошибка появляется после деплоя, проверьте зависимости, .env, кеш конфигурации и миграции:

php artisan config:clear
php artisan cache:clear
php artisan route:clear
php artisan view:clear
php artisan migrate --force

Для WordPress проверьте PHP-ошибки, плагины, тему, лимиты памяти и недавние изменения. Временное отключение проблемного плагина через переименование его каталога может вернуть сайт к работе, если лог указывает на фатальную ошибку PHP.

Практический сценарий для Node.js, Python и API-сервисов

Для приложения за reverse proxy порядок такой:

# 1. Проверить Nginx
sudo nginx -t
sudo systemctl status nginx

# 2. Проверить приложение
sudo systemctl status app
sudo journalctl -u app -n 100 --no-pager

# 3. Проверить порт
sudo ss -ltnp | grep ':3000'

# 4. Проверить ответ приложения локально
curl -I http://127.0.0.1:3000
curl http://127.0.0.1:3000/health

# 5. Проверить конфиг Nginx
sudo grep -R "proxy_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d

# 6. Применить исправления
sudo nginx -t
sudo systemctl reload nginx

Если приложение запускается через systemd, полезно проверить рабочую директорию, переменные окружения и команду запуска:

sudo systemctl cat app

Для Python-проектов с Gunicorn проверьте Unix-сокет или порт Gunicorn, число воркеров, права на сокет и ошибки импорта. Для Node.js проверьте NODE_ENV, порт, переменные окружения, менеджер процессов PM2 или systemd unit.

Отличие 502 от соседних ошибок помогает быстрее сузить поиск

Коды 500, 502, 503 и 504 часто путают. Разница помогает выбрать направление диагностики.

КодСмысл для администратораТипичный участок поиска
500Приложение вернуло внутреннюю ошибкуЛоги приложения, PHP, framework
502Nginx получил недействительный ответ от upstream или не смог корректно связаться с нимСокет, порт, backend, proxy/FastCGI
503Сервис временно недоступен или перегруженMaintenance, пул воркеров, лимиты
504Gateway не дождался ответа за отведённое времяДолгие запросы, сеть, БД, внешние API

MDN относит HTTP-статусы 500–599 к серверным ошибкам, а 502 описывает как ситуацию, в которой gateway или proxy получил недействительный ответ от upstream.

Для 502 главная зона поиска — связь между Nginx и backend. Для 504 акцент смещается на время ожидания. Для 500 чаще всего нужно смотреть само приложение.

Чек-лист восстановления сайта при 502

Используйте этот чек-лист как рабочий порядок на боевом сервере:

  1. Откройте страницу через curl -I, чтобы подтвердить код ответа.
  2. Посмотрите error.log Nginx за последние минуты.
  3. Найдите точную фразу рядом с upstream.
  4. Проверьте статус backend-сервиса.
  5. Проверьте порт или Unix-сокет.
  6. Сравните proxy_pass или fastcgi_pass с фактическим адресом backend.
  7. Проверьте права на сокет и каталоги проекта.
  8. Посмотрите логи приложения.
  9. Проверьте RAM, CPU, диск и число воркеров.
  10. Исправьте одну найденную причину.
  11. Запустите nginx -t.
  12. Примените reload.
  13. Повторно проверьте сайт и логи.

Команды для быстрой проверки:

curl -I https://example.com
sudo tail -n 100 /var/log/nginx/error.log
sudo systemctl status nginx
sudo nginx -t
sudo ss -ltnp
sudo journalctl -xe --no-pager

Профилактика снижает риск повторного 502 после релиза и обновлений

Чтобы 502 не появлялась после каждого деплоя, обновления PHP или перезапуска контейнеров, настройте базовую профилактику:

  • health-check endpoint в приложении, например /health;
  • мониторинг Nginx, PHP-FPM, приложения и базы данных;
  • алерты по количеству 5xx-ответов;
  • отдельные error log для каждого сайта;
  • проверку nginx -t в деплой-скрипте;
  • автоматический restart backend-сервиса через systemd при падении;
  • ограничение тяжёлых операций в веб-запросах;
  • фоновые очереди для экспорта, рассылок и генерации отчётов;
  • контроль свободной RAM и диска;
  • документирование версии PHP, пути сокета и схемы Docker-сети.

Пример systemd-настройки для автоперезапуска приложения:

[Service]
Restart=always
RestartSec=5

Для PHP-FPM полезно отдельно наблюдать за pm.max_children, медленными запросами, потреблением памяти и ошибками приложения. Для reverse proxy — за доступностью upstream-портов и временем ответа health-check.

Главный вывод для администратора

502 bad gateway nginx нужно разбирать как проблему связи Nginx с backend-сервисом. Самый быстрый путь к исправлению проходит через error log, проверку службы приложения, сокета или порта, затем через права, ресурсы и таймауты.

Если сайт уже лежит, начните с трёх команд:

sudo tail -n 100 /var/log/nginx/error.log
sudo systemctl status php8.3-fpm
sudo nginx -t

Для Node.js, Python, Go или Docker замените проверку PHP-FPM на статус своего приложения, контейнера или upstream-порта. После нахождения точной строки в логе причина обычно становится понятной: сервис остановлен, сокет изменился, порт закрыт, backend перегружен, заголовки слишком большие или приложение падает на конкретном запросе.

При использовании материалов сайта необходимо указывать ссылку на TGLand.ru. Если вы копируете фрагменты текста в интернете, прямая гиперссылка, доступная для индексации поисковыми системами, должна быть размещена в начале материала.

Вам также может понравиться