Ошибка 499 Client Closed Request означает, что клиент закрыл соединение раньше, чем сервер успел отправить ответ. Браузер, приложение, робот или прокси перестали ждать, а nginx записал в журнал код 499. Сам клиент этот код не получает: отвечать уже некому.
Мы собрали стенд из nginx 1.22, бэкенда на Python и PHP 8.3 и проверили, какие действия в Chromium 149 дают 499, что пишется в журнал и успевает ли сервер доделать работу. А затем замерили время ответа главных страниц 2056 сайтов, чтобы понять, у скольких из них посетитель может не дождаться.
Что такое HTTP 499
В стандарте протокола HTTP кода 499 нет, и поддержка его есть не у всех серверов. В реестре IANA диапазон 452–499 отмечен как Unassigned — не назначен. Код ввели разработчики nginx, и в исходном коде (файл ngx_http_request.h) объясняют зачем:
HTTP does not define the code for the case when a client closed the connection while we are processing its request so we introduce own code to log such situation when a client has closed the connection before we even try to send the HTTP header to it
Перевод: в HTTP нет кода для случая, когда клиент закрыл соединение, пока сервер обрабатывал запрос, поэтому nginx ввёл свой, чтобы записывать такое в журнал — когда клиент закрыл соединение ещё до того, как ему попытались отправить заголовок ответа.
Последние слова важны: код является служебной пометкой журнала, а не сообщение клиенту, и тела ответа у него нет. 499 появляется, только если клиент ушёл до отправки заголовков. Если ответ уже начал передаваться и клиент оборвал скачивание, в журнале будет обычный 200.
Тот же код использует и Azure Application Gateway. В документации Microsoft он называется «499 — клиент закрыл подключение»: ответ возникает, когда клиент закрыл или обновил приложение до завершения ответа или когда время ожидания на стороне клиента слишком мало.
499 — это ошибка клиента или сервера?
Формально — клиента: соединение закрыл он. На практике причина бывает на обеих сторонах:
- клиент ушёл сам — человек перешёл на другую страницу или закрыл вкладку, скрипт отменил запрос;
- клиент перестал ждать — у браузера, приложения, робота или прокси закончился таймаут, потому что сервер отвечал слишком долго.
Отличить одно от другого помогает время запроса в журнале — подробнее о нём в разделе про диагностику. Если 499 приходят через доли секунды — это обычное поведение пользователей. Если все они на одной и той же отметке, например ровно через 30 или 60 секунд, значит, перед вами чей-то таймаут, и сервер не успевает.
Стенд: что видно в журнале nginx
Мы настроили формат журнала с временем запроса ($request_time), временем ответа бэкенда ($upstream_response_time) и числом отправленных байт. Бэкенд отвечал через заданное число секунд.
| Сценарий | Код в журнале | Время | Что получил клиент |
|---|---|---|---|
| Клиент ждёт 2 с, бэкенд отвечает за 5 с | 499 | 2,002 с | ничего |
| То же с proxy_ignore_client_abort on | 200 | 5,001 с | ничего |
| proxy_read_timeout 3s, клиент ждёт 10 с | 504 | 3,000 с | страницу 504 |
| Обрыв скачивания файла 30 МБ через 2 с | 200 | 1,999 с | 2,1 МБ из 30 |
| Бэкенд отвечает за 1 с | 200 | 1,001 с | страницу |
В журнале ошибок nginx записал такое событие только на уровне info: «client prematurely closed connection, so upstream connection is closed too». При обычном уровне error этих строк не будет — и 499 видно только в журнале доступа.
Обрыв большого файла 499 не дал: заголовок 200 уже ушёл, и nginx записал 200 с числом переданных байт. Поэтому, если пользователи жалуются, что скачивание обрывается, искать нужно по размеру ответа, а не по коду.
Сервер доделывает работу после ухода клиента
Самый важный результат стенда. Бэкенд на Python в каждом случае отработал все 5 секунд и только при попытке отправить ответ получил ошибку «канал закрыт». То есть работа сделана, а результата никто не получил.
PHP повёл себя так же. Скрипт ждал 5 секунд и записывал заказ в файл; клиент перестал ждать через 2 секунды. В журнале nginx — 499, посетитель не увидел «Заказ принят», а заказ записался. Скрипт, который каждую секунду выводил по 4 КБ, тоже доработал до конца, и через nginx, и напрямую через Apache.
Документация PHP пишет: «По умолчанию скрипт завершает свою работу при отключении клиента», но там же уточняет, что разрыв PHP обнаруживает только при попытке что-либо вывести. На нашем стенде этого не случилось ни разу. Полагаться на прерывание скрипта нельзя.
Что это значит для сайта: человек нажал «Оформить заказ», страница долго грузится, он нажимает ещё раз. Первый запрос дал 499, но заказ создан. Второй — тоже. Отсюда двойные заказы, двойные письма и двойные списания. Защита — проверка на повтор на стороне сервера: один идентификатор формы — одна операция.
Какие действия в браузере дают 499
Мы открывали в Chromium страницу, которая отвечает через 5 секунд, и через секунду прерывали ожидание разными способами:
| Действие | Код в журнале |
|---|---|
| Переход на другую страницу | 499 |
| Кнопка «Стоп» | 499 |
| Закрытие вкладки | 499 |
| fetch с AbortController | 499 |
| Автопоиск: 5 запросов подряд, каждый отменяет прежний | 4 × 499 и 1 × 200 |
Последний сценарий встречается на сайтах с подсказками в поиске: каждое нажатие клавиши отправляет запрос и отменяет предыдущий. Браузер получил один ответ, а бэкенд отработал все пять — по 2 секунды каждый. Нагрузка на сервер выросла в пять раз, а в журнале четыре 499. Помогает задержка перед запросом: отправлять его, когда человек перестал печатать.
Почему возникают ошибки 499
По стенду и документации причины такие:
- Пользователи уходят со страницы. Обычное явление, единичные 499 тревоги не требуют.
- Страница отвечает медленно. Человек не дожидается и уходит; робот или приложение обрывают запрос по своему таймауту.
- Таймаут прокси, CDN или балансировщика меньше, чем у nginx. Если внешний прокси ждёт 30 секунд, а nginx — 60, при медленном ответе в журнале nginx будет 499, а посетитель увидит ошибку прокси.
- Скрипты на странице отменяют запросы. Автопоиск, повторные запросы, переходы в одностраничных приложениях.
- Плохая сеть. Мобильная связь или Wi-Fi обрывают соединение.
- Перегруженный сервер. Запросы стоят в очереди, клиенты не дожидаются, повторяют — и нагрузка растёт ещё больше.
Цепочка таймаутов
У nginx по умолчанию proxy_read_timeout, proxy_connect_timeout и proxy_send_timeout равны 60 секундам. Документация уточняет: таймаут чтения «устанавливается не на всю передачу ответа, а только между двумя операциями чтения».
Каждый участник цепочки должен ограничивать свой предел ожидания, и параметры нужно согласовать. Правильно настроенная цепочка выглядит так: каждое следующее звено ждёт меньше предыдущего. Клиент или внешний прокси ждёт дольше всех, nginx — меньше, бэкенд сам ограничивает время работы. Тогда при медленном ответе посетитель получит понятную страницу 504 от nginx, а не обрыв без ответа. Если же порядок обратный, клиент закрывает соединение первым — и вы получаете 499 в журнале и пустой экран у посетителя.
Например, Cloudflare, по его справке на 17.10.2026, ждёт ответа сервера 125 секунд и после этого показывает свою ошибку 524. Если nginx за ним ждёт бэкенд дольше, сервер продолжает работать над запросом, который никто уже не ждёт.
Пример: как 499 выглядит в журнале
Вот три строки из журнала нашего стенда в формате с временем запроса:
499 rt=2.002 urt=2.001 bytes=0 GET /slow/5 HTTP/1.1
504 rt=3.000 urt=3.000 bytes=167 GET /short/5 HTTP/1.1
200 rt=1.999 bytes=2129920 GET /big.bin HTTP/1.1
Первая строка указывает, что клиент ждал 2 секунды и ушёл, а nginx ничего не отправил: 0 байт. Вторая — что nginx сам перестал ждать бэкенд через 3 секунды и вернул 504. Третья — успешный статус 200, хотя клиент получил только часть файла: обрыв происходит уже после отправки заголовка, и nginx фиксирует 200.
Если вы видите в журнале только код и адрес, без времени, механизм обрыва не понять. Поэтому начинаем всегда с конфигурации журнала: это самый быстрый инструмент, и включить его безопасно. Дополнительную нагрузку такой формат почти не создаёт.
Как диагностировать 499 по журналу
-
Добавьте время в формат журнала. Без $request_time и $upstream_response_time причину не отличить:
log_format t '$status rt=$request_time urt=$upstream_response_time $request'; -
Выберите все 499 и посмотрите на время. Например:
awk '$9==499' access.log | sort | uniq -c. Если все 499 на одной отметке времени, это чей-то таймаут. -
Сравните адреса. Если 499 копятся на одном адресе — поиске, отчёте, оплате, — проблема в скорости именно этой страницы.
-
Проверьте клиентов. По полю User-Agent видно, кто закрывает соединения: браузеры, мобильное приложение, роботы поисковых систем или ваш собственный мониторинг.
-
Сверьте таймауты по цепочке. CDN, балансировщик, nginx, PHP-FPM, база данных — каждое звено со своим значением.
Как исправить ошибку 499
- Ускорьте медленные страницы. Это главное. Посмотрите, сколько отвечает сервер, и что именно занимает время: запросы к базе, внешние сервисы, генерация отчётов.
- Длинные операции выносите в фон. Отчёт, выгрузку, рассылку лучше ставить в очередь и сразу отвечать «принято, пришлём по готовности».
- Защитите формы от повтора. Один идентификатор операции — одна запись, даже если запрос пришёл дважды.
- Ограничьте ретраи и количество одновременных запросов. Когда клиенты повторяют запросы при задержке, перегруженный сервер получает ещё больше работы.
- proxy_ignore_client_abort on включайте осознанно. Он не даст nginx оборвать соединение с бэкендом, и в журнале будет код бэкенда вместо 499. Ответ клиент всё равно не получит, а сервер будет занят до конца.
- Проверьте зависимости. Медленный внешний сервис — платёжный шлюз, отправка писем, запрос к API — задерживает весь маршрут ответа. Задайте ему свой корректный таймаут, чтобы его отсутствие не держало страницу.
- Не повышайте таймауты вслепую. Такой подход неверно решает задачу: больший proxy_read_timeout не ускорит страницу, а только заставит посетителя ждать дольше.
Сколько сайтов отвечают дольше нескольких секунд
Мы запросили главные страницы 2056 сайтов из Германии с таймаутом 30 секунд и замерили время до первого байта ответа. Кодом 200 ответили 1474 сайта.
| Время до первого байта | Сайтов из 1474 |
|---|---|
| Больше 1 секунды | 272 |
| Больше 2 секунд | 81 |
| Больше 3 секунд | 37 |
| Больше 5 секунд | 14 |
| Больше 10 секунд | 1 |
Медиана — 0,5 секунды, у 90 % сайтов меньше 1,5 секунды. Ещё 41 сайт не ответил за 30 секунд вовсе. Именно на таких страницах посетители и роботы чаще всего не дожидаются ответа, и в журналах этих сайтов должны копиться 499. Сами коды 499 снаружи не видны — мы видим только время ответа.
Когда 499 можно игнорировать, а когда нет
Какой-то процент 499 есть в логах любого сайта: люди закрывают вкладки, переключаются на другой адрес, теряют сеть в метро. Это нормального поведения часть, и устранить её полностью нельзя. Важна не сама запись, а доля 499 среди всех запросов и её рост.
Стоит разобраться, если:
- доля 499 резко выросла после изменений на сайте или в инфраструктуре;
- 499 копятся на одном адресе, особенно на оплате, поиске или форме авторизации;
- время запроса у всех 499 совпадает с таймаутом какого-то звена — это верный признак, что клиент отказывается ждать по своему лимиту;
- вместе с 499 растут 502 и 504 — сервер перегружен;
- 499 приходят от роботов поисковых систем.
Если 499 мало, они разбросаны по разным страницам и приходят через доли секунды, это просто посетители, которые передумали. Тревога здесь не нужна: такое допустимо и ожидаемые ответы с кодом 200 составляют основную часть трафика.
Как мониторить 499 без шума
Считайте не число 499, а их процент от всех ответов за час и сравнивайте с обычным уровнем. Сигнал — рост в несколько раз, а не одиночный всплеск. Сохраняйте в журнал поле с временем запроса, чтобы сразу видеть, на какой секунде клиент сдался. Полезно вести отдельные метрики по ключевым адресам: оплата, корзина, поиск. Если ваш собственный мониторинг проверяет сайт с коротким таймаутом, он тоже создаёт 499 — исключите его из подсчёта по User-Agent или адресу.
Код 499 в веб-скрапинге и интеграциях
Ошибку 499 часто видят те, кто сам запускает программы сбора данных или интеграции с чужими сервисами. Здесь запрашивающая сторона — вы, и 499 в логах сервера значит, что ваш скрипт или прокси закрыл соединение слишком рано. Типичная причина — короткий таймаут клиента: 5 или 10 секунд на тяжёлый отчёт, который сервер формирует 20.
Что поможет:
- Определите, где появляется обрыв: в самом скрипте, в промежуточном прокси или на шлюзе.
- Сравните прямые запросы и запросы через прокси — у прокси свои лимиты на время ожидания.
- Увеличьте таймаут клиента под реальное время ответа, но не бесконечно.
- Ограничьте число попыток и параллельных запросов: бесконтрольные повторы при задержке только приводят к блокировке и росту нагрузки.
- Убедитесь, что получили настоящий ответ, а не страницу-заглушку защиты от роботов: такая страница может загрузиться быстро, но содержать совсем не те данные.
Пошаговый чек-лист, когда 499 внезапно вырос
- Сначала просмотрите логи: какие адреса, какое время, какие клиенты.
- Сопоставьте момент роста с изменениями: выкладка, новый плагин, настройки CDN, рост трафика.
- Проверьте скорость страниц, на которых копятся 499, и найдите медленную часть: база данных, внешний сервис, тяжёлый запрос.
- Сверьте таймауты по всей цепочке и согласуйте их: каждое внешнее звено должно ждать дольше внутреннего.
- Стабилизируйте нагрузку: включите кэш, ограничьте частые повторные запросы, вынесите долгие задачи в фон.
- Если сервер не справляется, его стоит масштабировать: добавить процессы PHP-FPM или ресурсы виртуального сервера.
- После исправления сравните долю 499 с тем, что было до правки.
Что делать посетителю
Если вы обычный пользователь и столкнулись с долгой загрузкой, ошибки 499 вы не увидите: браузер просто покажет пустую страницу или страницу другого сервера. Иногда помогает обновить страницу через минуту, проверить подключение к сети или открыть сайт с другого устройства. Если страница оплаты или заказа не загрузилась, не нажимайте кнопку повторно сразу — сначала проверьте почту или личный кабинет: как показал стенд, операция могла выполниться. Подтверждайте повтор, только убедившись, что первой записи нет.
Ошибка 499 и SEO
Поисковый робот, который закрыл соединение до получения ответа, ничего не получил, так что код 499 в индекс не попадает. Но если в журнале много 499 от YandexBot или Googlebot, страница отвечает дольше, чем робот готов ждать. Тогда он может не получить её вовсе и реже возвращаться к сайту. Проверьте такие адреса так же, как медленные страницы для людей.
Чем 499 отличается от 408, 502 и 504
- 408 Request Timeout — сервер не дождался запроса от клиента и сам закрыл соединение.
- 499 — клиент не дождался ответа сервера и закрыл соединение.
- 502 Bad Gateway — прокси получил от бэкенда неправильный ответ или не смог с ним соединиться.
- 504 Gateway Timeout — прокси не дождался ответа бэкенда за отведённое время.
Источники
- nginx: исходный код ngx_http_request.h, директивы proxy_ignore_client_abort, proxy_read_timeout, fastcgi_ignore_client_abort — nginx.org, github.com/nginx
- IANA: реестр кодов состояния HTTP — iana.org
- Microsoft: коды ответов Azure Application Gateway — learn.microsoft.com
- PHP: обработка соединений — php.net
Похожие ошибки
- Ошибка 504 — прокси не дождался ответа сервера.
- Ошибка 502 — прокси получил неправильный ответ.
- Ошибка 524 — Cloudflare не дождался ответа сервера.
- Ошибка 429 — сервер ограничил число запросов.
Границы замера
Стенд — nginx 1.22.1, Python 3.11, Apache 2.4.68 с PHP 8.3 в Docker, Chromium 149, всё на одной машине. Поведение PHP-FPM мы не проверяли. Обход 18.10.2026 из Германии, один запрос на главную страницу; время до первого байта включает перенаправления и установку защищённого соединения, а внутренние страницы сайтов могут отвечать медленнее главных.
Коротко
Ошибка 499 — запись nginx о том, что клиент закрыл соединение, не дождавшись ответа. Клиент её не видит, в стандарте HTTP её нет. Единичные 499 — обычное поведение пользователей. Много 499 с одинаковым временем — признак медленных страниц или неправильной цепочки таймаутов. Главное, что показал стенд: сервер доделывает работу и после ухода клиента, поэтому формы и платежи нужно защищать от повторов.
