Вместо сайта белая страница с надписью «502 Bad Gateway» или «504 Gateway Timeout» и подписью nginx внизу. Обе ошибки выдаёт nginx, и обе означают, что проблема не в нём самом, а в бэкенде — программе, которой nginx передал запрос. На сервере сайтов это почти всегда PHP-FPM.
Коротко: 502 — PHP-FPM не ответил вообще: он не запущен, у nginx нет доступа к сокету, все процессы заняты и очередь переполнена, или процесс PHP умер посреди запроса. 504 — PHP-FPM принял запрос, но не успел ответить за fastcgi_read_timeout (по умолчанию 60 секунд). Причину почти всегда называет одна строка в /var/log/nginx/error.log, а подробности — журнал PHP-FPM.
Чем 502 отличается от 504
Схема простая: nginx принимает запрос посетителя, видит, что это PHP, и передаёт его в PHP-FPM через unix-сокет (например, /run/php/php8.3-fpm.sock) или TCP-порт. Дальше возможны два сценария.
- 502 Bad Gateway — nginx не смог получить от бэкенда нормальный ответ: не подключился к сокету, соединение оборвалось или ответ оказался некорректным.
- 504 Gateway Timeout — соединение есть, запрос передан, но ответ не пришёл за отведённое время. Nginx закрывает соединение и отдаёт 504.
Отсюда первое правило диагностики: 502 — ищите, почему PHP-FPM недоступен или падает; 504 — ищите, почему PHP выполняется так долго.
Первая минута: служба, сокет, журнал
Имя службы зависит от системы. В Debian и Ubuntu для каждой версии PHP своя служба: php8.2-fpm в Debian 12, php8.3-fpm в Ubuntu 24.04, php8.4-fpm в Debian 13. В AlmaLinux и Rocky Linux — просто php-fpm. Ниже примеры для PHP 8.3 в Ubuntu.
systemctl status php8.3-fpm
ls -l /run/php/
tail -n 30 /var/log/nginx/error.log
В выводе systemctl status PHP-FPM показывает строку состояния:
Active: active (running) since Sat 2026-11-07 09:12:03 MSK; 5h 10min ago
Status: "Processes active: 5, idle: 0, Requests: 18342, slow: 0, Traffic: 2.40req/sec"
Здесь уже есть подсказка: idle: 0 — свободных процессов нет, все пять заняты. Если служба в состоянии failed, смотрите journalctl -u php8.3-fpm -n 50: обычно там ошибка в конфигурации пула после правки.
Где что лежит:
| Debian, Ubuntu | AlmaLinux, Rocky Linux | |
|---|---|---|
| Пулы | /etc/php/8.3/fpm/pool.d/*.conf |
/etc/php-fpm.d/*.conf |
| Сокет пула www | /run/php/php8.3-fpm.sock |
/run/php-fpm/www.sock |
| Журнал PHP-FPM | /var/log/php8.3-fpm.log |
/var/log/php-fpm/error.log |
| Проверка конфигурации | php-fpm8.3 -t |
php-fpm -t |
Что пишет nginx в error.log
Типичная строка выглядит так:
2026/11/07 14:22:31 [crit] 2051#2051: *8812 connect() to unix:/run/php/php8.3-fpm.sock failed (2: No such file or directory) while connecting to upstream, client: 198.51.100.7, server: example.com, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.3-fpm.sock:", host: "example.com"
Важна часть между адресом сокета и словом client. Обратите внимание на уровень: ошибки «нет файла» и «нет прав» nginx пишет как [crit], остальные — как [error]. Если для error_log задан уровень crit, сообщения уровня [error] в журнал не попадут — для диагностики оставьте уровень по умолчанию (error). Расшифровка:
| Строка в error.log | Что это значит | Что делать |
|---|---|---|
connect() to unix:… failed (2: No such file or directory) |
Сокета нет: PHP-FPM не запущен или путь в fastcgi_pass не совпадает с listen пула |
Запустить службу, сверить путь (часто после смены версии PHP) |
connect() to unix:… failed (13: Permission denied) |
У пользователя nginx нет прав на сокет | Проверить listen.owner, listen.group, listen.mode; в AlmaLinux — listen.acl_users и SELinux |
connect() to … failed (111: Connection refused) |
Адрес есть, но никто не слушает: PHP-FPM упал или слушает другой порт | Проверить службу и ss -xlp | grep php или ss -tlnp |
connect() to unix:… failed (11: Resource temporarily unavailable) |
Очередь сокета переполнена: все процессы заняты, новые запросы не помещаются | Найти, чем заняты процессы; при необходимости поднять pm.max_children |
upstream prematurely closed connection while reading response header from upstream |
Процесс PHP завершился посреди запроса: убит по request_terminate_timeout, упал или убит OOM killer |
Смотреть журнал PHP-FPM и journalctl -k |
upstream sent too big header while reading response header from upstream |
Заголовки ответа (часто cookies) больше буфера | Увеличить fastcgi_buffer_size и fastcgi_buffers |
upstream timed out (110: Connection timed out) while reading response header from upstream |
Это 504: PHP не ответил за fastcgi_read_timeout |
Искать медленный код или запрос, смотреть slowlog |
Про код 11 стоит сказать отдельно. Для unix-сокетов Linux возвращает EAGAIN вместо «соединение отклонено», когда очередь ожидающих соединений заполнена, — это прямо отмечено в исходном коде nginx. То есть ошибка означает не сломанный PHP-FPM, а перегруженный.
Журнал PHP-FPM: max_children и таймауты
grep -E 'max_children|timed out|exited on signal' /var/log/php8.3-fpm.log | tail -n 20
[07-Nov-2026 14:20:05] WARNING: [pool www] server reached pm.max_children setting (5), consider raising it
[07-Nov-2026 14:21:41] WARNING: [pool www] child 20411, script '/var/www/example.com/index.php' (request: "POST /index.php") execution timed out (90.004123 sec), terminating
[07-Nov-2026 14:21:41] WARNING: [pool www] child 20411 exited on signal 15 (SIGTERM) after 412.336981 seconds from start
Первая строка — пул упёрся в pm.max_children: новые запросы ждут в очереди сокета, а когда она переполнится, nginx начнёт отдавать 502. При pm = ondemand текст чуть другой — server reached max_children setting, без pm., поэтому в grep выше ищется просто max_children. Вторая и третья строки — запрос выполнялся дольше request_terminate_timeout, и PHP-FPM завершил процесс. Посетитель в этот момент получил 502.
Параметры пула, от которых зависят 502 и 504
pm и pm.max_children
Параметр pm задаёт режим управления процессами:
static— всегда ровноpm.max_childrenпроцессов;dynamic— число меняется междуpm.min_spare_serversиpm.max_spare_serversсвободных процессов, но не большеpm.max_children;ondemand— процессы запускаются при поступлении запросов и завершаются после простояpm.process_idle_timeout(по умолчанию 10 секунд).
pm.max_children — предел одновременно обрабатываемых запросов. В Debian и Ubuntu в стандартном пуле www он равен 5 — для рабочего сайта обычно мало. Но поднимать его наугад опасно: если процессам не хватит памяти, сервер уйдёт в своп, и станет только хуже. Прикиньте по памяти. Средний размер процесса:
ps -C php-fpm8.3 -o rss= | awk '{s+=$1; n++} END {printf "процессов: %d, в среднем: %.0f МБ\n", n, s/n/1024}'
процессов: 6, в среднем: 74 МБ
В AlmaLinux процесс называется php-fpm. RSS учитывает и общую память (например, opcache), поэтому оценка получается с запасом. Дальше считаем: из памяти сервера вычитаем то, что нужно MySQL, системе и кэшу файлов, остаток делим на средний размер процесса. Например, на VDS с 4 ГБ, где MySQL занимает около 1 ГБ, под PHP можно отдать примерно 2 ГБ — это около 27 процессов по 74 МБ, и pm.max_children = 25 будет разумным значением.
Помните: если процессы заняты ботами или медленными запросами, больший pm.max_children только отложит 502. Сначала выясните, чем они заняты.
Таймауты: max_execution_time, request_terminate_timeout, fastcgi_read_timeout
На время выполнения запроса влияют три настройки, и их стоит согласовать:
| Параметр | Где | Что делает |
|---|---|---|
max_execution_time |
php.ini или пул | PHP сам прерывает скрипт с ошибкой Maximum execution time of N seconds exceeded |
request_terminate_timeout |
пул PHP-FPM | PHP-FPM завершает процесс, если запрос идёт дольше; по умолчанию 0 — выключено |
fastcgi_read_timeout |
nginx | Сколько nginx ждёт данных от PHP-FPM между двумя чтениями; по умолчанию 60 с, после — 504 |
В Linux max_execution_time не учитывает время ожидания базы данных, сетевых запросов и sleep(), поэтому скрипт, который ждёт медленный SQL-запрос, может работать гораздо дольше заданного значения. Подробно об этом — в статье max_execution_time не срабатывает. Страховкой служит request_terminate_timeout, о чём прямо сказано в документации PHP-FPM.
Я обычно выстраиваю их по возрастанию: max_execution_time меньше request_terminate_timeout, а тот меньше fastcgi_read_timeout. Тогда зависший запрос завершает сам PHP-FPM и оставляет запись в своём журнале. Если же первым срабатывает таймаут nginx, посетитель видит 504, а процесс PHP, как правило, продолжает работать и занимать место в пуле.
; /etc/php/8.3/fpm/pool.d/www.conf
request_terminate_timeout = 120s
request_slowlog_timeout = 10s
slowlog = /var/log/php8.3-fpm-slow.log
# nginx, location с fastcgi_pass
fastcgi_read_timeout 150s;
Увеличивать fastcgi_read_timeout имеет смысл для отдельных долгих операций (импорт, отчёты), лучше в отдельном location, а не для всего сайта. Описание директивы — в документации nginx.
slowlog: что именно тормозит
Если задан request_slowlog_timeout, PHP-FPM для каждого запроса дольше этого времени записывает в slowlog стек вызовов PHP. В основном журнале появляется строка executing too slow (…), logging, а в slowlog видно, на какой функции и в каком файле плагина или темы висел запрос. Для 504 это самый быстрый способ найти виновника.
Статус-страница PHP-FPM
Чтобы видеть загрузку пула в любой момент, включите статус-страницу в пуле и перезапустите PHP-FPM:
pm.status_path = /fpm-status
Запросить её можно напрямую через сокет утилитой cgi-fcgi (пакет libfcgi-bin в Debian и Ubuntu, fcgi из EPEL в AlmaLinux), не трогая nginx:
SCRIPT_NAME=/fpm-status SCRIPT_FILENAME=/fpm-status REQUEST_METHOD=GET \
cgi-fcgi -bind -connect /run/php/php8.3-fpm.sock
pool: www
process manager: dynamic
start time: 07/Nov/2026:09:12:03 +0300
start since: 18624
accepted conn: 18342
listen queue: 12
max listen queue: 128
listen queue len: 511
idle processes: 0
active processes: 5
total processes: 5
max active processes: 5
max children reached: 3
slow requests: 0
Главное здесь: listen queue — сколько запросов ждут свободный процесс прямо сейчас, max children reached — сколько раз пул упирался в предел, slow requests — счётчик медленных запросов. Ненулевая очередь при idle processes: 0 — пул не справляется. Если открываете статус через nginx, ограничьте доступ (allow 127.0.0.1; deny all;): страница не должна быть видна из интернета.
Как исправить: порядок действий
- Проверьте конфигурацию перед перезапуском:
php-fpm8.3 -t(ответconfiguration file … test is successful) иnginx -t. - Нет сокета или отказ в соединении — запустите службу и сверьте путь в
fastcgi_passсlistenпула. После обновления PHP старый сокет часто остаётся прописан в конфигурации сайта; о версиях и путях — в статье Несколько версий PHP и PHP-FPM на одном сервере. - Permission denied — в Debian и Ubuntu nginx работает от
www-data, и у сокета пула должны бытьlisten.owner/listen.group, дающие ему доступ. В AlmaLinux nginx работает отnginx, а доступ задаётlisten.acl_users = apache,nginx; если сокет перенесён в нестандартное место, проверьте SELinux:ausearch -m avc -ts recent. - Код 11 и max_children — по статус-странице и журналу access.log выясните, чем заняты процессы. Если это живые посетители и память позволяет, поднимите
pm.max_children. - Too big header — увеличьте буферы:
fastcgi_buffer_size 32k;иfastcgi_buffers 16 16k;. - 504 — включите slowlog, найдите медленный код или SQL-запрос, а таймауты поднимайте только точечно.
Случай из практики
После перезагрузки одного из серверов я проверял скорость сайта. Замер через curl -o /dev/null -s -w '%{time_total}\n' https://example.com/ показала почти 6 секунд на первый запрос, а следующие обрабатывались примерно за 0,4 секунды. Ни 502, ни 504 не было, в журналах — тоже ничего.
Пул был настроен с pm = ondemand: после перезапуска в нём нет ни одного рабочего процесса, и PHP-FPM поднимает их по первым запросам. Но главное время уходило на «холодный» старт: opcache после перезапуска PHP-FPM пуст, и при первом обращении PHP заново компилирует все файлы движка, а после перезагрузки сервера ещё и кэш файлов и буферы MySQL пусты. Со второго запроса байт-код уже лежит в opcache, и время падает до обычного. Это не ошибка и не повод менять тариф. Кроме того, страничный кэш отдаёт готовые страницы без запуска PHP, поэтому большинство посетителей «холодного» запроса вообще не замечают.
Как не допустить повторения
- Рассчитайте
pm.max_childrenпо памяти и проверяйте статус-страницу при росте посещаемости. - Держите включённым
request_slowlog_timeout— медленный код будет виден до того, как дойдёт до 504. - Включите страничный кэш и opcache: чем меньше запросов доходит до PHP, тем реже пул упирается в предел. Пошаговая настройка связки — в статье Настройка VPS для быстрой работы сайта WordPress.
- Закрывайте на уровне nginx то, что сайту не нужно и что любят боты, — например,
xmlrpc.phpв WordPress. - После правки конфигурации всегда запускайте
php-fpm8.3 -tиnginx -t, а после перезапуска откройте сайт сами.
Частые вопросы
502 появляется только иногда, в остальное время сайт работает. Почему?
Чаще всего пул ненадолго упирается в pm.max_children: в пиковые минуты все процессы заняты, очередь сокета переполняется, и часть запросов получает 502 с кодом 11 в error.log. Ищите в журнале PHP-FPM строку server reached … max_children и сопоставьте время с access.log — нередко пик создают боты.
Достаточно увеличить fastcgi_read_timeout, чтобы убрать 504?
Ошибка исчезнет, но страница будет открываться так же долго, а процессы PHP будут дольше заняты, и при наплыве посетителей появятся 502. Поднимать таймаут стоит только для конкретных долгих операций. В остальных случаях найдите причину через slowlog и журнал медленных запросов MySQL.
Чем reload PHP-FPM отличается от restart?
В Debian и Ubuntu systemctl reload php8.3-fpm отправляет мастер-процессу сигнал USR2: он перечитывает конфигурацию и перезапускает рабочие процессы, не закрывая сокет. Restart останавливает службу полностью, и запросы, пришедшие в этот момент, получают 502. В обоих случаях opcache очищается, поэтому первые запросы после этого выполняются медленнее.
Ошибка 502 бывает, когда nginx работает как прокси, а не с PHP-FPM?
Да, логика та же: 502 — бэкенд недоступен или вернул некорректный ответ, 504 — не ответил вовремя. Разница только в директивах: вместо fastcgi_read_timeout — proxy_read_timeout, а в error.log в поле upstream будет адрес вида http://127.0.0.1:8080.