ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Nginx 499 Client Closed Request: причины, тайминги и как снизить процент обрывов

Код 499 в Nginx означает, что клиент закрыл соединение до ответа сервера. Это может быть браузер, мобильная сеть, gRPC‑клиент или балансировщик. Разберём диагностику по логам и таймаутам и меры, которые реально снижают долю 499.
Nginx 499 Client Closed Request: причины, тайминги и как снизить процент обрывов

Nginx-статус 499 Client Closed Request — один из самых «раздражающих» в эксплуатации: пользователи жалуются на «подвисания», в графиках растёт ошибка, а в логах нет привычного upstream-статуса 502/504. Важно понять главное: 499 — это не ответ Nginx клиенту. Это служебная отметка Nginx о том, что клиент оборвал запрос раньше, чем Nginx успел отдать ответ.

Под «клиентом» в цепочке может быть не только браузер. Очень часто «клиент» — это load balancer, CDN/WAF, ingress-контроллер, сервис-меш, мобильное приложение или gRPC-клиент. Поэтому при поиске причин 499 почти всегда нужно смотреть на всю цепочку таймаутов и на поведение сети.

Что означает nginx 499 и почему его нельзя «вылечить» только настройками Nginx

Если упростить, происходит так:

  • клиент открывает соединение и отправляет запрос;
  • Nginx начинает проксировать запрос в апстрим (или обрабатывать сам);
  • клиент по какой-то причине закрывает TCP-соединение (или HTTP/2 stream);
  • Nginx фиксирует событие и пишет в access log статус 499.

499 — это «client abort». Виноват не обязательно пользователь: часто это балансировщик или прокси, который не дождался ответа в свой таймаут и закрыл соединение.

Практическое правило: если доля 499 растёт, ищите, почему клиент перестал ждать. Иногда это «норма» (пользователь закрыл вкладку), но в продакшене чаще это симптом: запросы выполняются слишком долго, инфраструктура режет соединение по таймауту, либо клиентская сторона запускает отмены/ретраи.

Типовые причины Client Closed Request

1) Таймаут на стороне load balancer / CDN / ingress

Самый частый сценарий. Балансировщик/прокси имеет свой лимит ожидания ответа от бекенда или от Nginx. По истечении лимита он закрывает соединение к Nginx, а Nginx пишет 499.

Ключевой момент: увеличение proxy_read_timeout на Nginx может не помочь, если upstream (бекенд) отвечает долго, а LB timeout меньше. Таймауты нужно согласовывать по всей цепочке.

2) Долгие запросы и «маскировка» проблем под 499

Для пользователя это выглядит как «сайт завис, я обновил — и всё заработало». В логах при этом будет смесь 499 и иногда 504. Причина — долгий ответ бекенда: тяжёлый SQL, блокировки, медленный внешний API, GC-паузы, холодный кэш, CPU-throttle.

Нюанс: даже если проблема «в апстриме», в логах Nginx она может проявляться как 499, потому что до истечения таймаута Nginx клиент уже успел отвалиться. Поэтому важно смотреть реальные тайминги.

3) Мобильная сеть, Wi‑Fi, обрывы TCP и смена IP

Если у вас много трафика с мобильных клиентов, 499 — обычное явление. При смене сети или просадке качества соединения клиент может закрыть запрос и повторить его.

4) Браузер/фронтенд отменяет запрос (AbortController), навигация, автопоиск

Современные SPA/MPA активно отменяют запросы при смене маршрута, закрытии модалки, вводе в поиске (debounce) и т.д. Тогда вы увидите 499 на коротких запросах, причём на стороне бекенда они могут быть уже выполнены (особенно если отмена не прокидывается до приложения).

5) gRPC 499

В gRPC (часто поверх HTTP/2) клиент может отменить RPC по дедлайну, а Nginx (если он стоит в роли прокси) зафиксирует 499 как «client closed». Здесь особенно важны дедлайны на клиентах и таймауты на прокси/ingress: отмена — нормальный механизм, но массовые отмены обычно указывают на перегруз или неверно выставленные дедлайны.

6) Keepalive-настройки и пограничные кейсы

Параметр keepalive_requests ограничивает количество запросов на одном keepalive-соединении, после чего Nginx его закроет. Сам по себе keepalive_requests обычно не «создаёт» 499, но неудачные значения в сочетании с агрессивным реюзом соединений на клиентах/прокси могут давать «странные» паттерны обрывов на границах.

Пример формата access log в Nginx с request_time и upstream таймингами для диагностики 499

Как диагностировать 499 правильно: начинаем с таймингов в access log

Без таймингов 499 почти всегда превращается в гадание. Минимальный набор полей:

  • $request_time — сколько времени Nginx «жил» с запросом до записи в лог;
  • $upstream_response_time — сколько занял ответ апстрима (если апстрим успел ответить);
  • $upstream_connect_time и $upstream_header_time — где именно задержка: коннект или ожидание заголовков;
  • $upstream_addr и $upstream_status — какой апстрим участвовал и что он успел вернуть (если успел).

Пример формата лога с таймингами:

log_format timed '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent '
                 'rt=$request_time uct=$upstream_connect_time uht=$upstream_header_time urt=$upstream_response_time '
                 'uaddr=$upstream_addr ustatus=$upstream_status '
                 'ref="$http_referer" ua="$http_user_agent"';

access_log /var/log/nginx/access.log timed;

Как интерпретировать 499 по таймингам:

  • 499 и маленький rt (например, 0.02–0.2s): часто фронтенд отменил запрос, пользователь ушёл со страницы, либо прокси «переиграл» запрос (ретрай/перевыбор апстрима).
  • 499 и большой rt (например, 10–60s): клиент ждал долго и «сдался». Это либо реальная медлительность бекенда, либо внешний таймаут на LB/CDN/клиенте.
  • 499 и заполненный urt: апстрим мог даже ответить, но клиент уже закрыл соединение, пока Nginx читал или отдавал ответ.
  • 499 и пустой urt: Nginx мог не получить заголовки от апстрима (застрял на коннекте, в очереди, в ожидании данных) до момента закрытия клиентом.

Быстрая выборка 499 из логов

Даже с обычным combined-логом можно быстро оценить частоту и самые «шумные» URI:

awk '$9==499 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head

Если вы уже логируете rt=, удобно вытащить «длинные» 499 и посмотреть, что именно висит:

awk '$9==499 && $0 ~ /rt=/ {print $0}' /var/log/nginx/access.log | head

Если 499 коррелируют с CDN/прокси, полезно также проверить конфигурацию кеширования и поведение на длинных ответах. По теме CDN пригодится разбор версионирования кеша: как управлять кешем CDN и версионированием объектов.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Сверяем таймауты по цепочке: где рвётся ожидание

499 очень часто появляется из-за несогласованных таймаутов. Практично выписать цепочку и проставить значения (хотя бы «в порядке величин»):

  • таймаут клиента (браузер, мобильное приложение, SDK, gRPC deadline);
  • таймаут внешнего прокси (CDN/WAF);
  • таймаут балансировщика/ingress;
  • таймаут Nginx на чтение ответа апстрима: proxy_read_timeout (или grpc_read_timeout, fastcgi_read_timeout);
  • таймаут апстрима (например, gunicorn timeout, PHP-FPM request_terminate_timeout, app-level timeouts);
  • таймауты базы/очередей/внешних API.

Классическая ошибка: выставили proxy_read_timeout в 300s, но балансировщик режет на 60s. Тогда запросы, которые выполняются 70–120s, будут выглядеть как 499 на Nginx, хотя Nginx «готов ждать».

Какие директивы чаще всего участвуют (HTTP proxy)

  • proxy_connect_timeout — сколько ждать установления соединения к апстриму;
  • proxy_send_timeout — сколько ждать отправки запроса апстриму;
  • proxy_read_timeout — сколько ждать чтения ответа от апстрима;
  • send_timeout — сколько ждать отправки ответа клиенту (критично при медленных клиентах).

gRPC-таймауты в Nginx

Если вы видите 499 на gRPC, почти всегда стоит начать с дедлайнов на клиенте и сопоставления их с таймаутами на прокси/ingress и у бекенда. Когда дедлайн короче реального p95/p99, вы получите каскад отмен, ретраев и дополнительную нагрузку.

Когда повышать proxy_read_timeout, а когда это вредно

proxy_read_timeout — не «лекарство от 499», а инструмент. Повышать его уместно, когда:

  • запрос действительно должен выполняться долго (экспорт, отчёты, генерация архивов);
  • вы контролируете клиента и он готов ждать;
  • таймауты согласованы по всей цепочке (клиент → CDN/LB → Nginx → апстрим).

Повышать таймауты вредно, когда долгие запросы — следствие деградации. Тогда вы удерживаете больше воркеров/коннектов, растите очередь и получаете цепную реакцию: рост latency, рост отмен, рост ретраев.

Практика: выносите «долгое» в асинхрон

Если endpoint регулярно живёт десятки секунд, чаще всего лучше архитектурно:

  • перевести операцию в очередь;
  • отдавать 202 Accepted и task id;
  • давать прогресс/статус отдельным запросом;
  • для скачивания результата использовать подготовленный файл и быстрый ответ.
Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Как отличить «клиент ушёл» от «сервер тормозит»: признаки в логах

Сценарий A: фронтенд отменяет запросы

Признаки:

  • 499 группируются по одному пользователю/UA, часто на поиске/подсказках;
  • rt небольшой (десятки/сотни миллисекунд);
  • нет корреляции с ростом CPU/DB latency.

Сценарий B: таймаут балансировщика/прокси

Признаки:

  • много 499 с похожим rt (например, около 60.000 или 30.000);
  • ошибки идут «волнами» в одно и то же время;
  • часто совпадает с пиками трафика или деплоем.

Сценарий C: деградация апстрима/БД

Признаки:

  • rt растёт постепенно, распределение «тяжелеет»;
  • uht и urt большие;
  • параллельно растут 504, очереди в апп-сервере, время запросов в БД.

Схема цепочки таймаутов: клиент, балансировщик, Nginx и апстрим, где возникают 499

Точки, где 499 можно снизить «операционно»

1) Ограничьте ретраи и шторм запросов

Парадокс 499: клиент отвалился, но бекенд мог продолжить работу, а клиент/балансировщик повторил запрос. Для неидемпотентных операций это особенно болезненно. Проверьте:

  • есть ли ретраи на LB/CDN и как они настроены;
  • поведение SDK (особенно мобильных);
  • идемпотентность POST/PUT (idempotency key).

2) Настройте лимиты конкурентности в апстриме

Когда апстрим перегружен, запросы становятся длиннее, клиенты чаще «сдаются», и 499 растёт. Вместо бесконтрольного роста очереди лучше ограничивать конкурентность на приложении/воркерах и давать понятный backpressure (например, 503/429), если это допустимо вашим API/UX.

3) Следите за send_timeout и медленными клиентами

Если клиент медленно читает ответ (мобильная сеть, большой payload, скачивание), Nginx может долго удерживать соединение. Клиент может отвалиться, и вы получите 499. Проверьте:

  • размер ответов (не отдаёте ли мегабайты JSON);
  • уместность сжатия и пагинации;
  • стриминговые ответы (SSE/long-poll) и их таймауты.

4) Keepalive: не «крутить» без понимания

keepalive_requests помогает управлять жизненным циклом соединений. Если вы подозреваете проблемы на границе (особенно при L7-балансировке и большом количестве клиентов), меняйте параметр аккуратно и подтверждайте эффект метриками: количество соединений, клиентские ошибки, распределение latency.

Пошаговый чек-лист: что делать, когда 499 внезапно вырос

  1. Снимите долю 499 по vhost/URI и сравните с базовой линией.

  2. Проверьте access log timing: $request_time, $upstream_response_time, $upstream_status, $upstream_addr.

  3. Посмотрите распределение request_time именно для 499: короткие (cancel) или длинные (timeout/latency).

  4. Сверьте таймауты клиента, LB/CDN/ingress и Nginx (proxy_read_timeout и друзья). Ищите «ровные» значения (30s/60s/120s).

  5. Проверьте апстрим: CPU, очереди, пул соединений к БД, медленные запросы, блокировки, лимиты воркеров.

  6. Проверьте сетевые симптомы: потери пакетов, retransmits, перегруз на интерфейсе, conntrack (если есть NAT/фаервол).

  7. Отдельно проверьте gRPC: дедлайны клиента, HTTP/2 параметры, пики отмен.

  8. После изменений сравните: долю 499, p95/p99 latency, количество активных соединений, нагрузку на апстрим.

Полезные наблюдения из практики

  • 499 — это симптом. Лечите причину: долгие запросы, ретраи, несогласованные таймауты, перегруз.
  • «Ровный» request_time у 499 почти всегда указывает на внешний таймаут (LB/CDN/клиентский дедлайн).
  • Быстрые 499 на endpoint’ах подсказок/поиска часто нормальны, если нагрузка от них невелика и нет деградации апстрима.
  • Если 499 растёт вместе с latency, это почти всегда история про производительность, а не про «настройки Nginx».

Если вы активно используете CDN и ловите 499 на стыке SNI/нескольких доменов, иногда проблема выглядит как «обрывы» и странные ответы на уровне клиента. На всякий случай держите под рукой разбор про HTTP 421: как диагностировать 421 при SNI и связке CDN + Nginx.

Итог

Nginx 499 — это фиксация факта: client closed request. Под «клиентом» может скрываться браузер, приложение, gRPC-клиент или балансировщик. Самый быстрый путь к корню проблемы — включить access log timing, понять, короткие это отмены или длинные ожидания, а затем синхронизировать таймауты (proxy_read_timeout и таймауты прокси/LB) и устранить реальные источники задержек в апстриме.

Если хотите ускорить диагностику в вашей схеме, соберите 2–3 строки access log для 499 и 2–3 строки для «нормального» запроса (с rt, uct, uht, urt, ustatus) и выпишите цепочку прокси до приложения — по ним обычно быстро видно, где именно «сдаётся ожидание».

Для проектов на продакшене обычно удобнее держать Nginx рядом с приложением на отдельной машине/инстансе, чтобы проще масштабировать и изолировать нагрузку. Если вы выбираете площадку под такие задачи, можно посмотреть тарифы VDS или, для более простых сайтов, виртуальный хостинг.

Поделиться статьей

Вам будет интересно

Nginx/PHP-FPM: Too many levels of symbolic links (ELOOP) — как найти и исправить symlink loop при деплое OpenAI Статья написана AI (GPT 5)

Nginx/PHP-FPM: Too many levels of symbolic links (ELOOP) — как найти и исправить symlink loop при деплое

ELOOP («Too many levels of symbolic links») часто всплывает после деплоя через симлинк current в связке Nginx и PHP-FPM. Разберём ...
systemd-resolved и Docker: что происходит с /etc/resolv.conf и почему не работает DNS в контейнерах OpenAI Статья написана AI (GPT 5)

systemd-resolved и Docker: что происходит с /etc/resolv.conf и почему не работает DNS в контейнерах

Типичная поломка на Ubuntu/Debian: на хосте DNS работает, а в Docker-контейнерах появляется Temporary failure in name resolution. ...
Nginx: large_client_header_buffers и ошибка 400 Request Header Or Cookie Too Large OpenAI Статья написана AI (GPT 5)

Nginx: large_client_header_buffers и ошибка 400 Request Header Or Cookie Too Large

Ошибка 400 Request Header Or Cookie Too Large в Nginx обычно связана с раздутыми cookie или длинными заголовками из цепочки прокси ...