4xx Client error · nginx, non-standard

499 Client Closed Request

nginx logs 499 when the client hangs up before the response is ready.

What 499 means

A code nginx invented for its own logs. Nothing is ever sent with status 499: the client (a browser, a load balancer, a script with a timeout) closed the connection while nginx was still waiting on the upstream, so nginx records 499 instead of the status it never got to send.

A few 499s are normal: people close tabs and cancel requests. A run of them on one endpoint means something in front of nginx gives up sooner than the backend answers. Compare $request_time with $upstream_response_time in the access log to see how long the request had been running.

Common causes

  • A load balancer or proxy in front of nginx with a shorter idle timeout than the slowest backend response (an AWS ALB defaults to 60 seconds).
  • A client library or script timeout, for example a health check that waits 5 seconds against an endpoint that takes 8.
  • Users cancelling slow pages, or mobile clients losing the connection.
  • A slow or overloaded upstream: database locks, a cold cache, a stuck worker.

How to fix it

  • Make the endpoint faster, or move long work to a background job and return 202 Accepted.
  • Line up timeouts from the outside in: client, then load balancer, then nginx proxy_read_timeout, each a little longer than the slowest normal response.
  • If the backend must finish even when the client leaves (a payment, a write), set proxy_ignore_client_abort on; for that location.
  • Log $request_time and $upstream_response_time so every 499 shows how long it ran.

What it looks like

Nothing reaches the client. nginx notes the hang-up in its error log at the info level:

2026/10/01 10:13:01 [info] 31#31: *4821 epoll_wait() reported that client prematurely closed connection, so upstream connection is closed too while reading response header from upstream, client: 203.0.113.7, server: example.com, request: "GET /api/report HTTP/1.1", upstream: "http://10.0.3.14:8080/api/report"

The same event in an nginx access log (the status is the number after the request line):

203.0.113.7 - - [10/Sep/2026:10:12:01 +0000] "GET /api/report HTTP/1.1" 499 0 "-" "Mozilla/5.0"

Check it with curl

-i prints the status line and headers, and -w '%{http_code}' prints only the number, which is handy in scripts and health checks. Replace the URL with yours:

curl -sS -o /dev/null -w '%{http_code} %{time_total}s\n' https://example.com/api/orders

Compare what curl sees with what the browser sees. A different status from the same URL usually means a cache, a CDN edge or a cookie is in the way.

Investigating a run of 499s? Paste the log excerpt into Log Share to get line numbers, highlighting and an expiring link for whoever is on call with you.

  • 504Gateway Timeout: A proxy or load balancer gave up waiting for the upstream server.
  • 408Request Timeout: The client took too long to send the request.
  • 502Bad Gateway: A proxy or load balancer got an invalid response from the upstream server.

FAQ

Is 499 an official HTTP status code?

No. It is not in any RFC and is never sent over the wire. nginx uses it only in its logs, and some tools that read nginx logs (and some other proxies) have copied it.

Why do I see 499 in nginx but 504 at the load balancer?

The load balancer hit its own timeout first, returned 504 to the user and closed its connection to nginx. nginx saw the client leave and logged 499. Raise the load balancer timeout above the slowest normal response, or make that endpoint faster.

Should I alert on 499s?

Alert on the rate per endpoint, not on single events. A steady trickle is people closing tabs; a spike on one route is a slow backend or a timeout mismatch.