Решение проблем

Ошибка 504 Gateway Timeout: что она значит и как её исправить

Ошибка 504 Gateway Timeout (HTTP 504): прокси ждал ответа от сервера за ним и сдался. Что это значит, какой таймаут сработал и как ускорить медленную часть.

Опубликовано ~9 мин чтения
На этой странице

Коротко: ошибка 504 Gateway Timeout (код HTTP 504, «шлюз не дождался ответа») значит, что прокси или шлюз (nginx, Apache, балансировщик нагрузки или CDN) ждал ответа от сервера за ним и перестал ждать раньше, чем ответ пришёл. Бэкенд обычно жив, но слишком медленный. Посетителю стоит подождать и один раз обновить страницу. Владельцу сайта нужно найти медленный запрос, прежде чем увеличивать какой-либо таймаут.

Debian · Ubuntu · на сервере
# Какой запрос не дождался ответа и где?
sudo grep 'upstream timed out' /var/log/nginx/error.log | tail -n 5

# Как долго отвечает страница? (first byte = время до заголовков ответа)
curl -s -o /dev/null -w 'status %{http_code}  first byte %{time_starttransfer}s  total %{time_total}s\n' \
  https://example.com/slow-page

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

Что значит ошибка 504?

RFC 9110 определяет 504 так: сервер, «действуя как шлюз или прокси, не получил своевременного ответа от вышестоящего сервера, к которому ему нужно было обратиться, чтобы выполнить запрос». Шлюзом здесь выступает то, что стоит впереди: nginx перед PHP-FPM или приложением на Node, Apache с mod_proxy, балансировщик или CDN. Он передал запрос дальше, запустил отсчёт, и время вышло.

Отсюда следствие, которое часто упускают: бэкенд может продолжать работать над запросом и после 504 и даже успешно его завершить. Посетитель видит ошибку на заказе, импорте или сбросе пароля, которые на самом деле прошли.

Формулировка зависит от программы. nginx и HAProxy пишут 504 Gateway Time-out через дефис, Apache пишет Gateway Timeout: The gateway did not receive a timely response from the upstream server or application, а браузеры показывают страницу прокси как есть. Исключение составляет IIS с Application Request Routing: таймаут бэкенда он сообщает как 502.3, а не как 504.

504, 502, 408 и Cloudflare 524: в чём разница

ОшибкаКто перестал ждатьЧего ждалКуда смотреть
504 Gateway TimeoutВаш прокси: nginx, Apache, балансировщикОтвета бэкендаМедленные запросы, занятые воркеры, таймаут прокси
502 Bad GatewayВаш проксиНичего: ответ сломан или соединение не установилосьЗапущен ли бэкенд и доступен ли он?
408 Request Timeout (англ.)Веб-серверКогда клиент допишет свой запросСоединение клиента, медленные загрузки
Cloudflare 524 (англ.)CloudflareОтвета вашего сервера, по умолчанию 125 секундТот же медленный запрос, но дольше лимита Cloudflare
Cloudflare 522CloudflareХотя бы TCP-соединения с вашим серверомФайрвол, остановленный сервер, маршрутизация

Медленный запрос может закончиться и кодом 502: если бэкенд первым применяет собственный лимит, он убивает запрос и закрывает соединение, и прокси сообщает о сломанном ответе, а не о запоздавшем. Оба кода для одной и той же медленной страницы означают одну проблему.

Как исправить ошибку 504, если вы посетитель?

  • Подождите минуту и обновите страницу один раз. Часто 504 возникает из-за короткого всплеска нагрузки. Обновляя снова и снова, вы добавляете запросы в ту самую очередь, из-за которой ошибка и возникла.
  • Не отправляйте платёж или заказ повторно сразу. Сервер мог выполнить запрос уже после того, как прокси сдался. Сначала проверьте почту или страницу аккаунта: нет ли там подтверждения.
  • Запрашивайте меньше. Если ошибка возникает на большом экспорте, отчёте или поиске, более короткий период или меньшее число результатов часто укладываются в таймаут.
  • Исключите собственный прокси. Если таймаут на всех сайтах, шлюз на вашей стороне: VPN, корпоративный прокси или фильтрующее ПО. Попробуйте без него.

Владельцу сайта: какой таймаут сработал?

У каждого уровня между посетителем и вашим кодом свой таймер. Что увидит посетитель, решает самый короткий из них, и 504 дают лишь некоторые:

УровеньНастройкаПо умолчаниюПосетитель получает
nginx перед приложениемproxy_read_timeout60 с504 Gateway Time-out
nginx перед PHP-FPMfastcgi_read_timeout60 с504 Gateway Time-out
Apache mod_proxyProxyTimeout, иначе Timeout60 с504 Gateway Timeout
HAProxytimeout serverНет; HAProxy предупреждает об этом при запуске504 Gateway Time-out
CloudflareProxy Read Timeout125 с; поднять может только Enterprise524
PHPmax_execution_time30 с (только время работы скрипта)500 из-за фатальной ошибки PHP
PHP-FPMrequest_terminate_timeout0 (выключен)502, воркер убит
gunicorn--timeout30 с502, воркер убит

Значения по умолчанию взяты из документации каждого проекта; документация Cloudflare называет 125 секунд, хотя во многих старых руководствах до сих пор написано 100. Ваша конфигурация может отличаться, поэтому проверьте, что на самом деле загружено: sudo nginx -T | grep -n timeout для nginx, sudo apache2ctl -S плюс поиск Timeout в /etc/apache2/ для Apache.

Читаем строку лога

/var/log/nginx/error.log · пример
2026/10/21 09:14:03 [error] 2214#2214: *8841 upstream timed out (110: Connection timed out)
while reading response header from upstream, client: 198.51.100.7, server: example.com,
request: "GET /admin/export.csv HTTP/2.0", upstream: "http://127.0.0.1:3000/admin/export.csv"

while reading response header from upstream значит, что бэкенд принял запрос и ещё не начал отвечать, когда истекли 60 секунд. Это обычный случай: медленный код. while connecting to upstream значит, что бэкенд так и не принял соединение, а это указывает скорее на сеть или файрвол. Все варианты этой строки и настройки nginx подробно разобраны в статье о 504 в nginx. Apache записывает то же событие как (70007)The timeout specified has expired: AH01102: error reading status line from remote server.

Измерьте, куда уходит время

Опция -w у curl выводит время каждого этапа запроса. Ниже вывод одного запроса к example.com, выполненного нами с curl 7.88.1 в Debian 12.15:

Linux · macOS
curl -sI -o /dev/null -w 'status %{http_code}\ndns %{time_namelookup}s\nconnect %{time_connect}s\ntls %{time_appconnect}s\nfirst byte %{time_starttransfer}s\ntotal %{time_total}s\n' https://example.com
Вывод
status 200
dns 0.002221s
connect 0.003078s
tls 0.018462s
first byte 0.023165s
total 0.023196s

Каждое значение отсчитывается от начала запроса, поэтому смотреть нужно на разницу между ними. DNS, соединение и TLS на здоровом пути занимают миллисекунды; шаг от TLS до первого байта и есть время, которое сервер потратил на ответ, и именно о нём ошибка 504. Для настоящей проверки уберите -I, чтобы запрос был обычным GET, и направьте его на медленный URL.

Затем исключите прокси. На сервере обратитесь к бэкенду напрямую, с большим запасом по времени, чтобы curl не сдался первым:

на сервере · только бэкенд
curl -s -o /dev/null --max-time 300 \
  -w 'status %{http_code}  first byte %{time_starttransfer}s\n' \
  http://127.0.0.1:3000/admin/export.csv

Если бэкенду без прокси нужно 95 секунд, никакая настройка прокси не сделает страницу быстрой: проблема в работе, которая за ней стоит. Если напрямую он отвечает за секунду, а через прокси не успевает, смотрите на путь между ними: неверный адрес, файрвол или пул воркеров, который заполняется только при реальном трафике.

Чтобы проверить страницу извне своей сети, откройте проверку HTTP-заголовков (англ.): она запрашивает страницу с нашего сервера и показывает полученные код статуса и заголовки.

Почему бэкенд медленный?

Долгие запросы к базе данных

Самая частая причина на сайте, который раньше работал быстро. Таблица растёт, индекса нет, и запрос, занимавший 50 мс, теперь просматривает миллионы строк. Включите журнал медленных запросов в MariaDB или MySQL либо log_min_duration_statement в PostgreSQL и выполните EXPLAIN для худшего запроса. type: ALL в MariaDB или Seq Scan по большой таблице в PostgreSQL обычно значат, что не хватает индекса. Часто его добавление и есть всё исправление.

Тяжёлая работа внутри запроса

Экспорту, импорту, генерации PDF и картинок, сборке sitemap и массовой рассылке не место в запросе, которого ждёт человек. Как и задачам по расписанию: WordPress выполняет WP-Cron через запросы к wp-cron.php, а их порождают просмотры страниц, поэтому тяжёлая задача при каждом запуске занимает воркер PHP. Задайте define( 'DISABLE_WP_CRON', true ); в wp-config.php и вызывайте wp-cron.php вместо этого из системного crontab (англ.).

Заполненный пул воркеров

Каждый бэкенд обрабатывает одновременно фиксированное число запросов: PHP-FPM до pm.max_children (5 в пуле, который поставляют Debian и Ubuntu), gunicorn столько, сколько у него воркеров. Когда все заняты, новые запросы ждут в очереди, и это ожидание идёт в счёт таймаута прокси. Поэтому несколько медленных страниц могут довести до таймаута и быстрые. PHP-FPM в этот момент пишет в лог server reached pm.max_children setting. Node выполняет ваш JavaScript в одном потоке, так что один запрос с тяжёлой синхронной работой блокирует все остальные, пока не закончит.

Сеть или DNS за бэкендом

  • Внешние вызовы без таймаута. Запрос, который обращается к платёжному API, почтовому серверу или другому сервису, ждёт столько, сколько отвечает этот сервис. Дайте каждому HTTP-клиенту в коде явный таймаут.
  • Сломанный резолвер. Если DNS сервера медленный или не работает, каждый внешний вызов сначала ждёт, пока истечёт время разрешения имени. Этот случай разобран в статье Temporary failure in name resolution (англ.).
  • Бэкенд переехал. nginx разрешает имя хоста в proxy_pass один раз, при запуске или перечитывании конфигурации. Если IP бэкенда меняется, nginx продолжает подключаться к старому, и при отбрасываемых пакетах это заканчивается таймаутами while connecting to upstream до следующего перечитывания конфигурации.

Перегружен сам сервер

Если медленно открываются все страницы сразу, смотрите на машину: CPU на 100 %, высокий steal time или активный свопинг замедляют каждый запрос. Как найти процесс и отличить эти случаи друг от друга, разобрано в статье VPS high CPU usage (англ.).

Сначала ускорьте медленную часть, потом настраивайте таймауты

Увеличение таймаута лишь переносит сбой. С таймаутом 300 секунд запрос, которому нужно 400 секунд, всё равно упадёт, а пока он идёт, он держит воркер, которого ждут другие посетители. Действуйте в таком порядке:

  • Ускорьте запрос: индекс, кеш для страниц, одинаковых для всех, постраничный вывод для длинных списков.
  • Вынесите долгие задачи из запроса: сразу отвечайте 202 Accepted с идентификатором задачи, выполняйте её в фоновом воркере (очередь в вашем фреймворке или небольшая служба systemd), а страница пусть опрашивает результат или пользователю приходит письмо, когда результат готов.
  • Только потом поднимайте таймауты, и только там, где маршрут действительно должен работать долго, соблюдая порядок: собственный лимит приложения самый короткий, у прокси длиннее, у CDN самый длинный. Тогда самый внутренний уровень останавливает зависший запрос с понятной ошибкой в собственном логе.
nginx · длинный таймаут для одного маршрута
location /admin/export/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_read_timeout 300s;
}
Apache · то же через mod_proxy
ProxyPass        /admin/export/ http://127.0.0.1:3000/admin/export/ timeout=300
ProxyPassReverse /admin/export/ http://127.0.0.1:3000/admin/export/
Debian · Ubuntu · проверяем и применяем
sudo nginx -t && sudo systemctl reload nginx
sudo apache2ctl configtest && sudo systemctl reload apache2

Для PHP поднимите max_execution_time для этого скрипта и fastcgi_read_timeout выше него; три лимита PHP рядом показаны в статье о 504 в nginx.

Когда сервер просто слишком мал

Иногда код в порядке, а сервер нет. Сначала измерьте: htop (англ.) в медленный период покажет, сидят ли одно-два ядра на 100 %, пока остальные простаивают, или заканчиваются память и swap. Нагрузке, которая упирается в одно-два ядра, подходит тариф Hi-CPU KVM на AMD EPYC, где на тариф приходится меньше vCore. Сайту, которому не хватает воркеров PHP, потому что каждому нужна память, нужен тариф побольше с большим объёмом RAM, например старший уровень Standard KVM, где больше всего vCore на евро.

Частые вопросы

Что значит ошибка 504 Gateway Timeout?

Сервер, который работает шлюзом или прокси (nginx, Apache, балансировщик нагрузки или CDN), передал запрос дальше и не дождался вовремя ответа от сервера за ним. Бэкенд обычно работает, но слишком медленно, например из-за долгого запроса к базе данных или заполненного пула воркеров. nginx и HAProxy пишут эту ошибку как 504 Gateway Time-out.

Виноват ли посетитель в ошибке 504?

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

Нужно ли просто увеличить таймаут, чтобы исправить ошибку 504?

Только для маршрутов, которые действительно должны работать долго, и только после того, как запрос ускорен или работа вынесена в фоновую задачу. nginx по умолчанию ждёт 60 секунд (proxy_read_timeout и fastcgi_read_timeout), Apache тоже 60. Больший таймаут дольше держит воркер, и другие посетители стоят в очереди за ним, а за Cloudflare всё, что дольше 125 секунд, всё равно заканчивается ошибкой 524.

Читайте также

Все статьи раздела

504 Gateway Time-out в nginx: как найти медленный бэкенд и исправить ошибку

504 значит, что nginx ждал ответа от PHP-FPM, Node или другого бэкенда и не дождался. Читаем лог, увеличиваем нужный таймаут и чиним медленный запрос.

Ошибка 502 Bad Gateway: что это значит и как её исправить

Ошибка 502 Bad Gateway: прокси получил от приложения за ним неверный ответ или никакого. Что это значит, кто её показывает, что пишет лог и как исправить.

Ошибка 522 Cloudflare (Connection timed out): что делать

Ошибка 522: Cloudflare 19 секунд не мог достучаться до вашего сервера. Ищем причину на origin: файрвол, неверный IP в DNS, баны fail2ban или перегрузка.

Ошибка 429 Too Many Requests: что значит и как исправить в nginx, API и ботах

Ошибка 429 (HTTP 429 Too Many Requests) значит, что сработал лимит запросов. Как найти, чей он, настроить nginx limit_req и научить скрипт ждать Retry-After.

Ошибка 401 Unauthorized: что требует сервер и почему не доходят учётные данные

Ошибка 401 Unauthorized (HTTP 401): сервер ждёт действительные учётные данные и не получил их. Как прочитать WWW-Authenticate и починить auth_basic и токены.