breakitdown

499 Client Closed Request (nginx)

Answer

499 is not a real HTTP status. nginx writes it to its access log when the client closes the connection before nginx has sent a response. The client never receives a 499; it gave up first.

499
Client Closed Request nginx, unofficial

The client disconnected before nginx could send the response.

nginx records 499 in its access log; the client never receives it because it is already gone. It usually means the upstream was slow and the user, a browser navigation, or a client-side timeout gave up first. A rise in 499s is a latency signal: look at upstream response times.

Explained

Something on the client side ended the request: a user closed the tab or clicked away, a browser cancelled a superseded fetch, a mobile app or SDK hit its own timeout, or a load balancer in front of nginx timed out and dropped the connection.

The usual root cause is latency. If the upstream takes 30 seconds and the client's timeout is 25, every slow request shows up as a 499. Logging $request_time and $upstream_response_time in nginx's log_format shows how long those requests had been running.

By default nginx also aborts the upstream request when the client goes away. proxy_ignore_client_abort on; keeps the upstream request running, which helps when the upstream work must complete, at the cost of doing work nobody will see.

How to debug it

  1. Add $request_time and $upstream_response_time to the nginx log format and see how long 499 requests ran before the client left.
  2. Compare that duration with the client's timeout and with any load balancer or CDN idle timeout in front of nginx.
  3. Find the slow requests in the upstream logs and profile them.
  4. If 499s cluster on one endpoint, check whether the frontend cancels and reissues requests (for example on each keystroke in a search box); those are harmless.

Gotchas

Related explainers

← All HTTP status codes