Коротко: ошибка 504 Gateway Timeout (код HTTP 504, «шлюз не дождался ответа») значит, что прокси или шлюз (nginx, Apache, балансировщик нагрузки или CDN) ждал ответа от сервера за ним и перестал ждать раньше, чем ответ пришёл. Бэкенд обычно жив, но слишком медленный. Посетителю стоит подождать и один раз обновить страницу. Владельцу сайта нужно найти медленный запрос, прежде чем увеличивать какой-либо таймаут.
# Какой запрос не дождался ответа и где?
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 522 | Cloudflare | Хотя бы TCP-соединения с вашим сервером | Файрвол, остановленный сервер, маршрутизация |
Медленный запрос может закончиться и кодом 502: если бэкенд первым применяет собственный лимит, он убивает запрос и закрывает соединение, и прокси сообщает о сломанном ответе, а не о запоздавшем. Оба кода для одной и той же медленной страницы означают одну проблему.
Как исправить ошибку 504, если вы посетитель?
- Подождите минуту и обновите страницу один раз. Часто 504 возникает из-за короткого всплеска нагрузки. Обновляя снова и снова, вы добавляете запросы в ту самую очередь, из-за которой ошибка и возникла.
- Не отправляйте платёж или заказ повторно сразу. Сервер мог выполнить запрос уже после того, как прокси сдался. Сначала проверьте почту или страницу аккаунта: нет ли там подтверждения.
- Запрашивайте меньше. Если ошибка возникает на большом экспорте, отчёте или поиске, более короткий период или меньшее число результатов часто укладываются в таймаут.
- Исключите собственный прокси. Если таймаут на всех сайтах, шлюз на вашей стороне: VPN, корпоративный прокси или фильтрующее ПО. Попробуйте без него.
Владельцу сайта: какой таймаут сработал?
У каждого уровня между посетителем и вашим кодом свой таймер. Что увидит посетитель, решает самый короткий из них, и 504 дают лишь некоторые:
| Уровень | Настройка | По умолчанию | Посетитель получает |
|---|---|---|---|
| nginx перед приложением | proxy_read_timeout | 60 с | 504 Gateway Time-out |
| nginx перед PHP-FPM | fastcgi_read_timeout | 60 с | 504 Gateway Time-out |
| Apache mod_proxy | ProxyTimeout, иначе Timeout | 60 с | 504 Gateway Timeout |
| HAProxy | timeout server | Нет; HAProxy предупреждает об этом при запуске | 504 Gateway Time-out |
| Cloudflare | Proxy Read Timeout | 125 с; поднять может только Enterprise | 524 |
| PHP | max_execution_time | 30 с (только время работы скрипта) | 500 из-за фатальной ошибки PHP |
| PHP-FPM | request_terminate_timeout | 0 (выключен) | 502, воркер убит |
| gunicorn | --timeout | 30 с | 502, воркер убит |
Значения по умолчанию взяты из документации каждого проекта; документация Cloudflare называет 125 секунд, хотя во многих старых руководствах до сих пор написано 100. Ваша конфигурация может отличаться, поэтому проверьте, что на самом деле загружено: sudo nginx -T | grep -n timeout для nginx, sudo apache2ctl -S плюс поиск Timeout в /etc/apache2/ для Apache.
Читаем строку лога
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:
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.comstatus 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 самый длинный. Тогда самый внутренний уровень останавливает зависший запрос с понятной ошибкой в собственном логе.
location /admin/export/ {
proxy_pass http://127.0.0.1:3000;
proxy_read_timeout 300s;
}ProxyPass /admin/export/ http://127.0.0.1:3000/admin/export/ timeout=300
ProxyPassReverse /admin/export/ http://127.0.0.1:3000/admin/export/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.