Nginx Error 499: Client Closed Request — Причины и решение
В логе доступа Nginx (access.log) фиксируются запросы с нестандартным HTTP-статусом 499 и нулевым размером ответа (body_bytes_sent: 0). В error.log может появляться запись: client closed connection while waiting for request / upstream response.
| Сущность | Действие | Причина |
|---|---|---|
| HTTP-клиент (Браузер / Мобильное приложение) | Отправляет TCP FIN / RST (разрыв сессии) | Сработал локальный таймаут (например, в axios/fetch = 5000ms) или пользователь нажал «Отмена» |
| Upstream (Бэкенд / База данных) | Продолжает выполнять тяжелый запрос | Обработка занимает слишком много времени |
| Nginx | Фиксирует разрыв и записывает статус 499 | Nginx не успел передать ответ, так как сокет уже закрыт клиентом |
- Код 499 не отдается пользователю в браузер (пользователь видит сетевую ошибку или aborted request).
- На бэкенде накапливаются очереди зависших вычислений или неоптимизированных SQL-запросов.
- Локализуйте медленные эндпоинты в
access.log, отфильтровав их по статусу 499:awk '($9 ~ /499/)' /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -nr | head -n 10 - Увеличьте таймауты на стороне вызывающего клиента (Frontend / Mobile App / Microservice HTTP client), если операция действительно требует времени.
- Оптимизируйте скорость работы бэкенда (индексы в БД, кеширование в Redis), чтобы ответ отдавался быстрее, чем клиент сбросит сессию.
- Если бэкенд обязан гарантированно завершить операцию (например, финансовую транзакцию или оформление заказа), даже если клиент закрыл вкладку, включите в Nginx директиву
proxy_ignore_client_abort:location /api/payment/ { proxy_pass http://payment_backend; # Не разрывать соединение с бэкендом при отключении клиента: proxy_ignore_client_abort on; } - Проверьте конфигурацию и примените:
sudo nginx -s reload.
499 Client Closed Request — это проприетарный код, введенный создателем Nginx Игорем Сысоевым для явного различения в логах ошибок самого бэкенда (504 Gateway Timeout) от случаев, когда клиент самостоятельно оборвал ожидание.Частые вопросы (FAQ)
Почему в браузере не виден статус 499?
Потому что именно браузер инициировал закрытие TCP-соединения. Nginx не может отправить HTTP-ответ в уже закрытый сокет и лишь фиксирует этот факт в своем access.log.
Что делает директива proxy_ignore_client_abort on?
По умолчанию при разрыве связи с клиентом Nginx немедленно обрывает запрос к бэкенду. Директива proxy_ignore_client_abort on заставляет Nginx дождаться финального ответа бэкенда.
Может ли агрессивный Cloudflare таймаут вызывать 499 в Nginx?
Да. Таймаут Cloudflare составляет 100 секунд. Если бэкенд отвечает дольше, Cloudflare разрывает соединение с Nginx, и Nginx логирует этот запрос со статусом 499.
Как отличить 499 из-за пользователя от 499 из-за бага фронтенда?
Если 499 происходит ровно через 5000ms или 10000ms после старта запроса — это жесткий таймаут в конфигурации HTTP-клиента фронтенда (например, timeout в axios).