499 Client Closed Request (nginx)
This is an educational reference page about HTTP 499 Client Closed Request. The page itself is served as 200 OK so it can be indexed as HTTP documentation.
Used by nginx to log a request whose client closed the connection before nginx sent the response header.
Specification
Not registered in the IANA HTTP Status Code Registry. nginx defines it as NGX_HTTP_CLIENT_CLOSED_REQUEST because HTTP has no code for a client that closed the connection.
What it means
HTTP 499 Client Closed Request is a non-standard code. nginx logs it when the client closes the connection before nginx sends a response.
Common causes
- The client timeout is shorter than the upstream response time, so the client gives up first.
- A load balancer or proxy in front of nginx has a shorter timeout and closes its connection to nginx.
- The user closed the tab or navigated away, or the app cancelled an in-flight request, such as a search box replacing the previous query.
- A health check or monitoring probe with a short timeout.
Where you see it
- nginx access logs, when the client disconnects before nginx sends the response header. Nothing is sent back, so the client never receives this 499.
- Google's API error model maps the gRPC code CANCELLED to HTTP 499, and grpc-gateway writes 499 as the response status for a cancelled call. A client that is still connected, such as one whose call a backend cancelled, can receive this 499.
- ASP.NET Core (StatusCodes.Status499ClientClosedRequest) and Armeria (HttpStatus.CLIENT_CLOSED_REQUEST) define constants for it. Armeria maps gRPC CANCELLED to 499.
How to fix it
- Log $request_time and compare it with the client and load balancer timeouts for 499 entries. Values that match a timeout point to a timeout mismatch.
- Speed up the slow upstream, or raise the client or load balancer timeout above the upstream's normal response time.
- Set proxy_ignore_client_abort on only when the upstream must finish its work after the client leaves. nginx then keeps the upstream connection open.
- Expect a small rate of 499s from cancelled browser requests. Alert on a jump, not on every 499.
Examples
nginx log format with request time
log_format timed '$remote_addr "$request" $status '
'$body_bytes_sent rt=$request_time';
access_log /var/log/nginx/access.log timed;
203.0.113.7 "GET /api/reports/export HTTP/1.1" 499 0 rt=60.002
Common mistakes
- Using 499 for a server-side timeout. Use 408 when the server timed out waiting for the request, or 504 when an upstream timed out.
- Counting every 499 as a server error. Many come from users or apps that cancelled on purpose.
- Expecting a client to read a 499 response from nginx. The connection is already gone.
- Treating 499 as a standard status. HTTP clients and libraries outside these platforms may not know it.
Developer notes
499 is not in the IANA registry. The primary source is the comment above NGX_HTTP_CLIENT_CLOSED_REQUEST in nginx's src/http/ngx_http_request.h: HTTP does not define a code for a client that closed the connection, so nginx introduced its own code to log that case. Other platforms reuse the number for a cancelled request.
Questions
What does HTTP 499 mean?
HTTP 499 Client Closed Request means the client closed the connection before the server responded. nginx logs it. It is not a standard HTTP status code.
Does the client receive a 499?
Not from nginx. The client has already disconnected, so the 499 exists only in the access log. A gRPC gateway can send 499 as a real status when a call ends with CANCELLED and the client is still connected.
Is 499 a client error or a server problem?
The code is in the 4xx range because the client ended the request. The cause is often a slow upstream or a timeout mismatch, so check $request_time on 499 entries before deciding.
How do I fix 499 errors in nginx?
Find out why clients give up. Compare $request_time with client and load balancer timeouts, speed up slow upstream responses, and use proxy_ignore_client_abort only when the upstream must finish its work.
Why does gRPC map CANCELLED to 499?
Google's API error model maps CANCELLED, an operation cancelled by the caller, to HTTP 499 Client Closed Request. grpc-gateway and Armeria use the same mapping.
See also
- 408 Request Timeout
- 504 Gateway Timeout
- nginx source: ngx_http_request.h
- google.rpc.Code: CANCELLED maps to 499