Ошибка 504 — это ошибка терпения. Посредник отправил запрос дальше, подождал сколько велено и сдался. Главная улика здесь не текст страницы, а секундомер: 504 приходит через точное, заранее настроенное время. Мы собрали стенд, где можно замедлить приложение как угодно, и замерили, когда и от кого эта ошибка приходит.
Что говорит определение
Стандарт HTTP (RFC 9110, раздел 15.6.5): сервер, действуя как шлюз или прокси, не получил своевременного ответа от вышестоящего сервера, к которому обратился, чтобы выполнить запрос.
Справочник кодов Яндекса:
Сервер, при работе в качестве внешнего шлюза или прокси-сервера, своевременно не получил отклик от вышестоящего сервера, к которому он обратился, пытаясь выполнить запрос.
И совет там же:
Увеличьте таймауты в настройках прокси-сервера или шлюза. Проверьте производительность вышестоящих серверов и оптимизируйте их работу.
Как и 502, эту ошибку рисует исправный посредник, а тормозит звено за ним. При 502 следующее звено ответило плохо, при 504 — не успело ответить. А 500 — это когда посредника нет и сломалось само приложение.
Стенд: когда приходит 504
Собрали nginx 1.22.1 перед приложением, которое умеет отвечать с любой задержкой, по байту или обрываться на полуслове. Таймауты на стенде короткие: 3 секунды на соединение, 5 секунд на ответ. Для каждого случая засекли, что и через сколько получает посетитель и что nginx пишет в журнал ошибок.
| Что делает приложение | Ответ | Через сколько | Строка в журнале ошибок |
|---|---|---|---|
| Отвечает за 4 секунды | 200 | 4,0 с | — |
| Отвечает за 6 секунд | 504 | 5,0 с | upstream timed out … while reading response header |
| Адрес не отвечает вовсе | 504 | 3,0 с | upstream timed out … while connecting to upstream |
| Отвечает за 65 секунд, таймауты по умолчанию | 504 | 60,1 с | … while reading response header |
| Шлёт заголовки по байту раз в полсекунды | 504 | 5,0 с | … while reading response header |
| Шлёт тело по килобайту раз в 4 секунды | 200 | 40,0 с | — |
| Отдал заголовки и треть тела, замолчал | 200 | 5,0 с | upstream timed out … while reading upstream |
Из таблицы следуют три вещи.
Время 504 — подпись таймаута. Ошибка пришла через 5,0 секунды при настройке «5», через 3,0 — при таймауте соединения «3», через 60,1 — при настройках по умолчанию. 504 ровно через минуту — таймаут nginx по умолчанию. Через 30 или 100 секунд — ищите звено с такой настройкой.
Ожидание заголовков считается целиком, а тела — по кускам. Приложение, которое отдавало заголовки по байту, получило 504 на пятой секунде, хотя пауза между байтами была полсекунды. А приложение, которое отдавало тело по килобайту раз в 4 секунды, спокойно отдавало страницу 40 секунд: каждая пауза короче пяти, и nginx ждал. Документация nginx описывает таймаут как паузу между двумя чтениями; на нашей версии для заголовков он работал как общий срок.
Обрыв посреди страницы не превращается в 504. Посетитель получил код
200 и объявленную длину 10 026 байт, а байт не получил ни одного: всё, что
успело прийти, осталось в буфере nginx. В журнале ошибок при этом та же
строка upstream timed out, только с концом while reading upstream.
Проверка, которая смотрит на код, такую страницу сочтёт исправной.
Два приложения за балансировщиком: 504 вдвое дольше
Частая схема — два экземпляра приложения за одним nginx. Мы поставили оба медленными и спросили страницу.
| Запрос | Ответ | Через сколько | Сколько экземпляров спросили |
|---|---|---|---|
| GET | 504 | 10,0 с | оба, по 5 секунд каждый |
| POST | 504 | 5,0 с | один |
| Следующий запрос сразу после двух таймаутов | 502 | 0,0005 с | ни одного |
На GET nginx после таймаута повторил запрос на второй экземпляр, и
посетитель ждал удвоенное время. POST он не повторяет: запрос мог уже
что-то изменить. А после того как оба экземпляра не ответили, nginx на
10 секунд пометил их нерабочими, и следующий посетитель получил
мгновенную 502 с записью no live upstreams. Одна и та же медленная
страница за полминуты даёт в журнале и 504, и 502.
Цепочка из двух посредников: чей это 504
Между посетителем и приложением часто стоят два nginx: защита от атак или хостинг снаружи, свой сервер внутри. У каждого свой таймаут. Мы собрали обе расстановки: снаружи 3 секунды и внутри 10, и наоборот. Приложение отвечало за 6 секунд.
| Кто нетерпеливее | Ответ посетителю | Журнал внешнего | Журнал внутреннего |
|---|---|---|---|
| Внешний (3 с) | 504 через 3,0 с | 504 и строка upstream timed out | 499, строки об ошибке нет |
| Внутренний (3 с) | 504 через 3,0 с | 504, строки об ошибке нет | 504 и строка upstream timed out |
Посетитель в обоих случаях видит одно и то же. Различаются журналы. Код
499 — внутреннее обозначение nginx: «клиент закрыл соединение, не
дождавшись». Если во внутреннем журнале 499 там, где снаружи 504, — не
дождался внешний посредник, и его таймаут короче, чем время вашей
страницы. Строку upstream timed out пишет только тот, кто сдался.
Сколько ждут живые сайты
Чтобы понять, далеко ли живые сайты от этих порогов, мы засекли время до первого байта ответа у 260 сайтов компаний Екатеринбурга — два круга с перерывом в десять минут. Обычным браузером главную страницу открыли в обоих кругах 234 сайта; по ним и считаем.
| Первый байт позже, чем | Хотя бы в одном круге | В обоих кругах |
|---|---|---|
| 1 секунда | 112 | 52 |
| 3 секунды | 32 | 8 |
| 5 секунд | 25 | 6 |
| 10 секунд | 4 | 0 |
| 60 секунд | 0 | 0 |
Середина — 0,96 секунды по худшему из двух кругов. До минуты не дотянул никто: самый долгий ответ пришёл через 50,4 секунды, а в другом круге тот же сайт ответил за 3,5. Порог Яндекса в 3 секунды перешагнули 32 сайта хотя бы раз и 8 — оба раза.
Медленность здесь — не свойство сайта, а свойство минуты. Из 32 сайтов, отвечавших дольше 3 секунд, 24 в другом круге укладывались в норму. Разовый замер скорости поэтому говорит мало: одна страница бывает быстрой и медленной с разницей в десять минут.
504 в двух кругах не пришла ни разу. Зато два сайта за общей службой защиты от атак в одну секунду ответили 502 через 14,6 и 14,8 секунды, а десятью минутами раньше — 200 за четверть секунды. Судя по времени, посредник не дождался ответа, но назвал это 502. Служба защиты рисует коды по своим правилам, и время здесь надёжнее кода.
Как найти, кто не дождался
- Засеките время. Через сколько секунд пришла 504 — столько стоит таймаут у сдавшегося звена. Ровно 60 — nginx по умолчанию.
- Посмотрите подпись страницы с ошибкой. Строка внизу («nginx», название защиты от атак или хостинга) называет того, кто её нарисовал.
- Найдите строку
upstream timed outв журнале ошибок. Окончание говорит, на каком шаге кончилось терпение:while connecting— приложение не принимает соединения,reading response header— долго думает,reading upstream— начало отдавать и застряло. - Сравните с журналом внутреннего сервера. 499 в нём при 504 снаружи — таймаут внешнего звена короче вашей страницы.
- Найдите медленную страницу, а не таймаут. Поднять таймаут просто, но посетитель и робот от этого ждут дольше, а не получают быстрее.
Что 504 значит для поиска
Для Яндекса страница начинает считаться медленной задолго до 504. В справке о диагностике сайта:
Если некоторые страницы загружаются больше 3 секунд, робот фиксирует это как ошибку. Из-за медленной работы сервера индексирование сайта может задерживаться.
Это ошибка «Долгий ответ сервера» в Вебмастере. Значит, при таймауте nginx по умолчанию в 60 секунд страница, которая отвечает за 10 секунд, 504 никогда не получит, а в диагностике Яндекса уже будет с ошибкой. 504 — поздний сигнал: к моменту, когда он появился, медленная страница давно видна роботу.
Чего мы проверить не смогли
- Сколько ждёт сам робот Яндекса. Справка называет порог ошибки в 3 секунды, но не говорит, через сколько робот разрывает соединение.
- Другие серверы и службы защиты. Apache, балансировщики облаков и службы защиты от атак считают таймауты по-своему; стенд — только nginx.
- Ожидание заголовков в других версиях nginx. То, что оно считалось общим сроком, замерено на 1.22.1.
- PHP-FPM. У него свой таймаут на выполнение скрипта, и при нём посредник может получить не тишину, а обрыв — то есть 502, а не 504.
Границы замера
Стенд — nginx 1.22.1 с короткими таймаутами (3 и 5 секунд) и одним замером на таймауты по умолчанию. Приложение — наша заглушка, которая управляет задержкой; настоящее приложение тормозит неравномерно.
Обход — 260 сайтов Екатеринбурга, два круга 24 сентября, запросы с одного адреса в России. Время до первого байта включает дорогу и рукопожатие, поэтому на долях секунды оно завышено; на секундах разница несущественна. Пять сайтов в первом круге не установили соединение за 70 секунд, все в одном двухминутном окне, а во втором круге ответили за 0,6–2,4 секунды — это похоже на сбой нашей дороги, а не их, и в таблицу они не вошли. Как время ответа складывается из частей и чем мерить его точнее — в разборе скорости отдачи сервера.
Коротко
- 504 рисует посредник, который не дождался. Приложение за ним может быть живым, но медленным.
- Время ошибки называет таймаут: ровно 60 секунд — nginx по умолчанию.
- nginx ждёт заголовки целиком, а тело — по кускам. Страница, которая застряла посреди тела, приходит с кодом 200 и пустая.
- Два экземпляра за балансировщиком удваивают ожидание GET, а после двух таймаутов следующий посетитель получает 502.
- 499 во внутреннем журнале при 504 снаружи — не дождался внешний посредник.
- Яндекс считает ошибкой ответ дольше 3 секунд — задолго до любого 504.
