Хостинг

Как исправить 504 Gateway Timeout в Nginx

Определяем, где закончился таймаут, проверяем приложение напрямую и ищем задержку по журналам Nginx.

Песочные часы между прокси и сервером приложения обозначают таймаут

504 Gateway Timeout означает, что шлюз не дождался ответа следующего звена. В схеме «клиент → CDN → Nginx → приложение → база» ошибку может вернуть несколько компонентов. Начните с проблемного маршрута и времени запроса, затем сравните ответ прокси с ответом приложения. Так станет ясно, какой таймаут сработал и где запрос провёл время.

Найти прокси, который вернул 504

Сохраните время, метод, адрес маршрута и идентификатор запроса, если приложение его выдаёт. Для безопасной проверки используйте читающий GET без токенов в URL и без повторной отправки платежа или формы.

curl -sS --max-time 75 -o /dev/null \
  -w 'status=%{http_code} connect=%{time_connect}s ttfb=%{time_starttransfer}s total=%{time_total}s\n' \
  https://example.com/catalog/
sudo tail -n 80 /var/log/nginx/error.log
sudo journalctl -u myapp.service --since '15 min ago' --no-pager

ttfb показывает время до первых байтов ответа. При status=000 HTTP-ответ не получен. Например, мог закончиться лимит самого curl. Проверьте stderr и код выхода клиента.

Если перед Nginx стоит CDN, сравните ответ через CDN и прямой запрос на известный IP своего origin. --resolve оставляет домен в SNI и заголовке Host, поэтому проверка сертификата сохраняется. Пример использует адрес из диапазона для документации.

curl -sS --max-time 75 --resolve example.com:443:203.0.113.10 \
  -o /dev/null -w 'origin=%{http_code} ttfb=%{time_starttransfer}s\n' \
  https://example.com/catalog/

Если прямой вход ограничен адресами CDN, выполните проверку из разрешённой сети или используйте журналы origin. Открывать origin всему интернету для диагностики не требуется. Подробнее о цепочке доставки — в обзоре CDN.

Прочитать причину в журнале Nginx

Запись Что проверить
while connecting to upstream Адрес и порт, слушающий сокет, firewall, сеть до приложения и очередь соединений
while reading response header from upstream Ожидание заголовков. Проверьте воркеры приложения, запросы в БД и внешние вызовы
while reading upstream Пауза при передаче ответа. Проверьте поток данных, генерацию файла или внешний сервис
connect() failed (111: Connection refused) На целевом порту нет слушателя или соединение отвергнуто. Частый результат — 502

Для HTTP-upstream проверьте тот же путь напрямую на сервере. Подставьте реальный адрес, Host и маршрут с учётом переписывания URI в proxy_pass. У FastCGI-сокета другой протокол, поэтому такой curl подходит HTTP-сервисам вроде Gunicorn, Node.js или Uvicorn.

sudo ss -ltnp '( sport = :8001 )'
curl -sS --max-time 75 -H 'Host: example.com' \
  -o /dev/null -w 'app=%{http_code} ttfb=%{time_starttransfer}s\n' \
  http://127.0.0.1:8001/catalog/

Быстрый ответ /health/ подтверждает доступность процесса. Для поиска задержки повторите именно проблемный читающий запрос. Если зависает только конкретная страница, ищите её SQL-запросы, внешний API или тяжёлую обработку. Если одновременно медленны даже лёгкие страницы, проверьте заполнение очереди, воркеры и ресурсы VPS. Для отказа соединения есть отдельный разбор 502.

Добавить измерение времени upstream

Если задержки периодические, добавьте log_format в контекст http, а access_log — в нужный server. Такой формат записывает URI без query string. Заголовки авторизации и cookies в него не входят.

log_format upstream_timing '$remote_addr "$request_method $uri" '
    '$status rt=$request_time us=$upstream_status '
    'uct=$upstream_connect_time uht=$upstream_header_time '
    'urt=$upstream_response_time';
access_log /var/log/nginx/myapp-timing.log upstream_timing;

uct — подключение к приложению, uht — получение заголовка, urt — время ответа upstream. rt охватывает обработку запроса целиком со стороны Nginx. При нескольких попытках upstream в поле может быть несколько значений. Для нового файла настройте ротацию и срок хранения.

Согласовать таймауты после диагностики

proxy_read_timeout ограничивает интервал между чтениями из upstream. Поток, который регулярно отдаёт данные, может продолжаться дольше этого значения. Для PHP-FPM аналогичный параметр называется fastcgi_read_timeout. Отдельно учитывайте таймаут клиента, CDN, самого приложения и внешней базы.

Если операции действительно нужно больше времени и длительность укладывается в лимит внешнего прокси, измените настройку только для её маршрута. Пример для HTTP-upstream:

location /reports/ {
    proxy_set_header Host $host;
    proxy_pass http://127.0.0.1:8001;
    proxy_connect_timeout 3s;
    proxy_read_timeout 45s;
}

Значения иллюстративные. Проверьте, что они подходят приложению и воркерам. Затем выполните sudo nginx -t && sudo systemctl reload nginx и повторите тот же GET. Для отчёта, которому нужны минуты, обычно удобнее фоновая задача с номером и страницей статуса. Такой подход освобождает веб-воркер и позволяет пользователю проверить результат без повторной тяжёлой операции.

На стенде сервер приложения задерживал заголовки примерно на две секунды. При proxy_read_timeout 1s Nginx вернул 504 и записал upstream timed out. Тот же GET с лимитом в четыре секунды вернул 200.

Первоисточники

Нашли неточность?

Сообщите адрес статьи и фрагмент через контакты редакции.