504 от Nginx — один из самых раздражающих инцидентов: внешне «упал сайт», а внутри часто всё живо, просто кто-то кого-то не дождался. Почти всегда внизу лежит конкретный таймер: Nginx ждёт бэкенд, клиент ждёт Nginx, балансировщик ждёт Nginx, а приложение ждёт базу.
Ниже — практический разбор типовых источников nginx 504, как отличить Gateway Timeout от «клиент ушёл» (499), какие директивы реально влияют (proxy_read_timeout, fastcgi_read_timeout, uwsgi_read_timeout, grpc_read_timeout) и как крутить таймауты так, чтобы не замаскировать проблему, а стабилизировать сервис.
Что именно означает 504 в Nginx
Код 504 Nginx выдаёт, когда работает в роли шлюза (reverse proxy) и не получил своевременный ответ от «следующего звена» (upstream): приложения, PHP-FPM, uWSGI, gRPC-сервиса или соседнего прокси.
Часто в error log рядом будет ключевая фраза: upstream timed out. Это не «Nginx не отвечает», а «upstream слишком долго молчит».
Увеличение таймаутов иногда оправдано (экспорт, отчёты, long polling), но чаще это временная мера. Если upstream зависает из-за БД, локов, CPU или IO — вы просто дольше ждёте ту же проблему и увеличиваете число висящих соединений.
Быстрый чеклист: куда копать в первую очередь
Чтобы не утонуть в настройках, начните с трёх проверок:
Есть ли в error log строка
upstream timed out? Если да — это 504 из-за ожидания ответа upstream.Кто разорвал соединение первым? Если в access log 499 — ушёл клиент (браузер/балансировщик/мобильная сеть), а не Nginx.
Какая технология upstream?
proxy_pass(HTTP),fastcgi_pass(PHP-FPM),uwsgi_pass(uWSGI),grpc_pass(gRPC). Для каждой — свой набор*_timeout.
Если сайт живёт на общем сервере и под нагрузкой регулярно ловит пики по CPU/IO, самый быстрый способ убрать «соседский фактор» — вынести Nginx и приложение на отдельные ресурсы (например, на VDS) и уже там измерять тайминги «в чистом виде».

Логи, без которых 504 чинится вслепую
Минимум, который нужен для нормальной диагностики: расширенный access log + error log. Идея простая: вы должны видеть, где именно «сгорает время» — на коннекте, на отправке запроса или на ожидании заголовков/тела ответа.
1) Добавляем тайминги в access log
Если у вас ещё нет лог-формата с таймингами — добавьте. Особенно ценны $request_time и $upstream_response_time: они показывают, сколько времени запрос жил на стороне Nginx и сколько времени «внутри» занял ответ upstream.
log_format timed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'rt=$request_time urt=$upstream_response_time '
'uct=$upstream_connect_time uht=$upstream_header_time '
'ua="$http_user_agent" '
'upstream="$upstream_addr"';
access_log /var/log/nginx/access.log timed;
Как читать:
rtблизок к вашему таймауту (например, 60.000) и статус 504 — Nginx упёрся в ожидание.urtпустой или «-» — часто означает, что до ответа upstream не дошло (например, коннект/рукопожатие не состоялось).urtбольшой, а статус 200 — запрос просто долгий (возможно, его обрывает внешний слой, тогда вы увидите 499).
2) Находим конкретное сообщение в error log
Типовая запись:
upstream timed out (110: Connection timed out) while reading response header from upstream
Ключевые слова:
while connecting to upstream — проблема на этапе подключения (влияет
proxy_connect_timeout).while reading response header from upstream — upstream принял запрос, но не отдал даже заголовки вовремя (влияет
proxy_read_timeout/fastcgi_read_timeout/uwsgi_read_timeout/grpc_read_timeout).while sending request to upstream — Nginx долго отправляет запрос upstream (влияет
proxy_send_timeout; часто всплывает при медленной сети, перегрузке или большом request body).
Главные таймауты Nginx: что за что отвечает
У Nginx несколько таймеров, и путаница здесь — причина «покрутил не то, стало хуже». Важно понимать: read-timeout — это не «весь запрос не дольше N секунд», а ожидание активности на чтении.
proxy_connect_timeout
Максимальное время на установление TCP-соединения с upstream (а в некоторых сценариях — на первичное рукопожатие). Если upstream живой, но перегружен, коннекты могут зависать в очередях (accept queue) и срабатывать как таймаут коннекта.
Симптом: upstream timed out ... while connecting to upstream.
proxy_read_timeout
Таймаут ожидания между операциями чтения от upstream. Если upstream не прислал данные в течение этого времени — Nginx завершит запрос 504.
Для долгих вычислений, где приложение «думает», но ничего не отдаёт (даже заголовки), именно этот таймаут чаще всего и стреляет.
proxy_send_timeout
Таймаут на отправку запроса upstream. Актуально, когда клиент присылает большой request body, Nginx проксирует его дальше, а соединение до upstream медленное или upstream читает медленно.
Аналоги для других upstream
PHP-FPM через FastCGI:
fastcgi_read_timeoutuWSGI:
uwsgi_read_timeoutgRPC:
grpc_read_timeout
Логика у них похожая: сколько Nginx готов ждать данных/ответа от соответствующего протокола.
Почему вместо 504 вы видите 499 (и это тоже про таймауты)
499 — не стандартный HTTP-код, а внутренний статус Nginx: «клиент закрыл соединение». В реальности клиентом может быть:
браузер пользователя (закрыл вкладку, ушёл со страницы);
мобильная сеть или прокси на пути;
внешний балансировщик или ingress (у него свой таймаут ожидания ответа от Nginx);
мониторинг/бот, который не готов ждать.
Частая ловушка: вы увеличили proxy_read_timeout до 300s, Nginx перестал отдавать 504, но в логах пошли 499. Это означает: upstream по-прежнему отвечает долго, просто теперь клиент сдаётся раньше, чем Nginx.
При массовых 499 сравнивайте
rtиз access log с таймаутами внешнего слоя (балансировщик/ingress/CDN). Еслиrtоколо 30s, а Nginx ждёт 60s — «режет» именно внешний слой.
Keepalive и «случайные» таймауты под нагрузкой
keepalive_timeout влияет на время удержания idle-соединения с клиентом. Сам по себе он не «лечит» 504, но влияет на нагрузочный профиль: слишком большой keepalive при большом числе клиентов может раздувать количество открытых соединений и потребление памяти и файловых дескрипторов.
Что проверить:
хватает ли лимитов на файловые дескрипторы (симптомы: странные обрывы, проблемы с accept);
не упираетесь ли в лимиты соединений upstream, если используете keepalive к upstream-пулу;
нет ли перекоса, когда клиенты держат много «пустых» keepalive, а рабочим запросам не хватает ресурсов.
Buffering: как он связан с 504 и длинными ответами
Тема buffering важна в двух сценариях: большие ответы и «потоковые» ответы (SSE/long polling/частичные данные).
Proxy buffering
При включённом буферинге Nginx читает ответ upstream быстрее (если может), складывает в буферы или временные файлы и отдаёт клиенту. Это часто полезно: upstream освобождается раньше, а медленный клиент не держит ваше приложение «на поводке».
Но есть обратная сторона: если вы ожидаете периодические небольшие «пульсы» данных, а где-то по пути они буферизуются, клиент может не увидеть данных вовремя. Тогда внешний слой решит, что «тишина» — и оборвёт соединение (часто это выглядит как 499).
FastCGI buffering
Для PHP-FPM похожая история: большие ответы, временные файлы, задержка выдачи клиенту. Сама по себе буферизация не должна вызывать 504, но может маскировать то, что приложение слишком долго молчит до первых байт (а это уже fastcgi_read_timeout).

Long polling и «вечные» запросы: как согласовать таймауты
Long polling и часть realtime-паттернов — легитимная причина держать запрос открытым долго. Но важно согласовать таймауты на всех слоях:
Nginx как reverse proxy (например,
proxy_read_timeout);таймауты приложения/воркера (чтобы оно само не убивало запрос раньше);
таймауты внешнего балансировщика/ingress;
таймауты клиента (JS fetch/XHR, мобильные SDK).
Если хотя бы одно звено настроено на 30s, а остальные на 120s — получите «рваную» картину: часть запросов 499, часть 504, часть успешных, и кажется, что проблема плавающая.
Если у вас gRPC и есть промежуточные прокси, полезно свериться с практиками проксирования HTTP/2: как устроить gRPC-web через прокси.
Практические конфиги: куда добавлять таймауты
Ниже — типовые фрагменты. Подставляйте значения осознанно: сначала измерьте фактические времена ответов и решите, это «нормально долго» или «ненормально зависло».
1) HTTP upstream через proxy_pass
location /api/ {
proxy_pass http://app_upstream;
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering on;
}
2) PHP-FPM через fastcgi_pass
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_read_timeout 60s;
}
Если у вас «длинные» PHP-скрипты (экспорт/импорт), важно согласовать таймауты и на стороне PHP-FPM (например, лимиты выполнения запроса в пуле), иначе Nginx будет ждать, а воркер уже завершён.
3) uWSGI
location / {
include uwsgi_params;
uwsgi_pass unix:/run/uwsgi/app.sock;
uwsgi_read_timeout 60s;
}
4) gRPC
location /my.service.v1.API/ {
grpc_pass grpc://grpc_upstream;
grpc_read_timeout 60s;
}
Почему таймауты «вдруг» стали срабатывать: типовые причины
Если раньше работало, а потом пошли Gateway Timeout, обычно причина не в Nginx как таковом. Частые источники:
Деградация базы данных: блокировки, рост времени запросов, вакуум/чекпоинты, нехватка индексов.
Очереди/воркеры забиты: запросы стоят в очереди приложения, а Nginx честно ждёт.
CPU steal или нехватка CPU на виртуализации: процесс есть, но «квантов» не дают — задержки растут.
Диск и IO: логирование, своп, медленный storage, переполнение временных директорий.
Сетевые проблемы: потери пакетов, MTU/PMTUD, проблемы с DNS у резолва upstream (если upstream задан именем).
Неправильная буферизация для realtime: соединения держатся, но «пульса» нет, внешний слой обрывает.
Как отличить «нужно увеличить таймаут» от «нужно чинить производительность»
Опирайтесь на факты из логов:
Если
$upstream_response_timeстабильно чуть больше текущего таймаута, а бизнес-сценарий легитимно долгий — таймаут можно увеличить, но параллельно подумайте о фоновой обработке (job queue) и отдаче статуса выполнения.Если времена ответа скачут (то 0.2s, то 60s, то 120s) — это почти всегда ресурсная деградация или блокировки. Таймауты не лечат.
Если 504 появляется только при пиках — ищите очереди, лимиты воркеров и насыщение CPU/IO.
Мини-ранбук: шаги диагностики 504/499 за 15 минут
Найдите в error log строки
upstream timed outи зафиксируйте стадию: connecting, sending, reading.Сопоставьте по времени с access log и полями
rt,urt,uct,uht.Если много
499— определите, кто «клиент» (браузер или внешний LB/ingress). Проверьте его таймауты ожидания ответа.Проверьте здоровье upstream: количество воркеров, очереди, логи приложения на медленные операции.
Если упираетесь в ресурсы сервера — проверьте CPU/IO/память, своп, лимиты файловых дескрипторов.
Только после этого меняйте
proxy_read_timeout/fastcgi_read_timeoutи фиксируйте эффект по метрикам.
Частые ошибки при «лечении» 504
Поднять только
proxy_read_timeoutи забыть про внешний балансировщик: 504 уйдёт, но вырастут 499.Сделать таймауты слишком большими везде: при реальной деградации получите лавинообразный рост одновременных запросов и усилите проблему.
Не логировать тайминги: без
$upstream_response_timeсложно понять, где именно тратится время.Игнорировать «первые байты»: приложение может считать 2 минуты и только потом прислать заголовки — это классический триггер read-timeout.
Что делать, если проблема упирается в ресурсы
Если вы видите, что 504 коррелирует с ростом нагрузки и upstream физически не успевает, вариантов обычно два:
оптимизация: ускорить запросы, разгрузить БД, вынести тяжёлые операции в очередь, добавить кеширование;
масштабирование: больше CPU/RAM, быстрый диск, отдельные ноды под приложение/БД.
На практике для продакшена удобнее, когда Nginx и upstream живут на предсказуемых ресурсах, а лимиты процессов и воркеров не упираются в «соседей» по серверу. В этом же месте полезно держать единые таймауты на всем периметре и регулярно проверять их при изменениях (новый ingress, новый CDN, новые настройки LB). Для смежной темы про «долго и безопасно» при переездах и смене точки входа пригодится материал: миграция сайта без простоя.
Итоги
Nginx 504 Gateway Timeout — это симптом несогласованных ожиданий по времени. Лечится не магическим увеличением чисел, а связкой: тайминги в логах → понимание стадии таймаута → настройка нужной директивы (proxy_connect_timeout, proxy_send_timeout, proxy_read_timeout или fastcgi_read_timeout/uwsgi_read_timeout/grpc_read_timeout) → проверка внешних таймаутов и поведения клиентов (включая 499) → работа с первопричиной в upstream.


