504 Gateway Time-out / upstream timed out (110: Connection timed out) while reading response header from upstream
#Что происходит
Nginx передаёт запрос в PHP-FPM и ждёт ответа не дольше fastcgi_read_timeout (по умолчанию 60 секунд). Если PHP не успел, nginx отвечает 504, в журнале появляется upstream timed out. Сам PHP-FPM может прервать процесс по request_terminate_timeout, и тогда будет 502. Увеличение таймаутов лишь скрывает симптом: нужно найти, почему скрипт выполняется долго — тяжёлый запрос к базе данных, внешний API, блокировка файла, цикл без ограничения.
#Причины
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Медленный SQL-запросНет индексов. | Slowlog показывает mysqli_query/PDO::query. | Добавьте индексы, оптимизируйте запрос. |
| Внешний API недоступенНет таймаутов в cURL. | Slowlog показывает curl_exec. | Задайте таймауты, кеш. |
| Тяжёлая операция в запросеИмпорт, генерация отчётов. | Всегда около лимита. | Вынесите в очередь. |
| Нехватка процессовОчередь запросов. | Много 504 в пики. | См. настройку max_children. |
| БлокировкиСессии PHP, файлы. | Параллельные запросы одного пользователя висят. | session_write_close(), Redis для сессий. |
| Перегрузка сервераCPU/диск. | Всё медленно. | Мониторинг, апгрейд. |
#Порядок
- Определите, какие URL медленныеНа сервере с доступом SSH откройте терминал и посмотрите журнал доступа nginx, если он уже содержит
$request_timeи$upstream_response_time. Если нет — попросите администратора добавить отдельный формат времени ответа (пример ниже) после резервной копии конфигурации иnginx -t. Воспроизведите медленное обращение, сравните URL, время и код HTTP. Не делайте вывод о причине из одного случайного долгого запроса. - Включите slowlog в пуле PHP-FPMНайдите конфигурацию активного пула PHP-FPM (
/etc/php/8.2/fpm/pool.d/*.confна части Debian/Ubuntu; версия и путь могут отличаться). Сохраните копию файла. Добавьте в секцию нужного пулаrequest_slowlog_timeout = 5sиslowlog = /var/log/php8.2-fpm.slow.log, предварительно убедившись, что каталог доступен службе. Проверьте синтаксисsudo php-fpm8.2 -tи выполнитеsudo systemctl reload php8.2-fpm; на хостинге с панелью меняйте параметры через неё. - Прочитайте трассировки: они покажут функцию, в которой «застрял» скриптВоспроизведите медленный URL и выполните
sudo tail -n 60 /var/log/php8.2-fpm.slow.logпо реальному пути файла. Найдите время запроса и строки PHP backtrace: путь и функция показывают, где выполнение задержалось, но не всегда первопричину. Если журнал пуст, проверьте, что изменён именно работающий пул, порог 5 секунд превышался и PHP-FPM смог создать файл. Не публикуйте журнал с путями и персональными данными. - Проверьте медленные запросы MySQLЕсли трассировка указывает на работу с базой, откройте журнал медленных запросов MySQL/MariaDB. Не включайте глобальные параметры на чужом сервере без согласования: при наличии прав администратора сначала проверьте
SHOW VARIABLES LIKE "slow_query_log";и путь журнала; при необходимости включите запись на короткий период и оцените её размер. Используйте EXPLAIN для конкретного медленного SELECT на копии или в безопасном режиме. Не добавляйте индексы вслепую. - Проверьте внешние запросыЕсли в трассировке видны
curl_exec, HTTP-клиент или запрос к API, найдите соответствующий адрес в настройках приложения и проверьте его доступность с сервера, напримерcurl -I --max-time 10 https://адрес-сервиса/(замените только на реальный доверенный адрес). Сравните время ответа и сообщение об ошибке; 403/401 — не таймаут, а отдельная причина. Не отключайте проверку TLS и не сохраняйте ключи API в открытых журналах. - Временно увеличьте таймауты в nginx и PHP-FPM для длинных задачТаймауты увеличивайте только если установлено, что запрос действительно должен долго работать и серверу хватает ресурсов. В конфигурации nginx меняется
fastcgi_read_timeout, в пуле PHP-FPM —request_terminate_timeout; это разные ограничения. Сохраните старые значения, проверьте синтаксис (nginx -t,php-fpm8.2 -t) и примените перезагрузку конфигурации каждой службы отдельно. Если нагрузка растёт или запрос зависает бесконечно — верните прежние значения. - Для тяжёлых операцийДля регулярного формирования отчётов, импорта и рассылки лучше перенести длительную задачу из веб-запроса в фоновую очередь либо задание cron с контролем доступа. Сначала убедитесь, что приложение поддерживает этот способ: произвольный запуск веб-скрипта из cron может дублировать операции. Настройте расписание в панели хостинга, команду с абсолютным путём к нужной версии PHP и отдельный журнал выполнения, затем проверьте одно тестовое задание.
- Проверьте нагрузку CPU/диска и количество процессовВо время замедления посмотрите загрузку сервера: в SSH выполните
uptime,free -h,df -hиps -eo pid,pcpu,pmem,cmd --sort=-pcpu | head; команды ничего не меняют. Сопоставьте процессы PHP с количеством запросов и сообщениямиpm.max_childrenв журнале. Если память заканчивается или есть ожидание диска, увеличение числа процессов усугубит ситуацию; сохраните замеры и проверьте приложение и базу данных. - Команды для отдельных ситуацийВыберите только подходящий вариант. Это не одна последовательность: часть команд диагностирует проблему, а часть меняет настройки или данные.
#Когда увеличение таймаутов оправдано
Для импортов и резервных копий допустимо поднять таймаут именно для их адреса: отдельный location /admin/import { fastcgi_read_timeout 600s; }. Для остального сайта оставьте умеренные значения. Бесконечные таймауты исчерпывают пул процессов и делают сайт уязвимым к перегрузке.