What 499 means
499 is not a standard HTTP status code. It's nginx's own code (NGX_HTTP_CLIENT_CLOSED_REQUEST) for one situation: the client closed the connection while nginx was still waiting to send the response. Nobody receives a 499, since the client has already gone; nginx only writes it to the access log:
203.0.113.7 - - [05/Oct/2026:10:14:02 +0000] "GET /report HTTP/1.1" 499 0 "-" "Mozilla/5.0 ..."
The 0 bytes sent is typical. Other servers record the same event differently, and a few products use 499 for unrelated things, but in a web server log it nearly always means nginx.
Common causes
- Users giving up: closing the tab, navigating away, or pressing reload while a slow page loads.
- Cancelled requests in the page:
AbortController, search-as-you-type that cancels the previous request, or a single-page app changing route. - Client timeouts shorter than the backend: a mobile app that waits 10 seconds for an endpoint that takes 15, or a load balancer in front of nginx whose idle timeout runs out first. The user then sees an error from that client or load balancer (often a 504), while nginx logs 499.
- Health checks and monitors with short timeouts hitting a slow endpoint.
- A slow upstream: database locks, cold starts or an overloaded app, which makes every other cause above more likely.
A few 499s are normal on any busy site. A spike on one endpoint means it's too slow.
How to fix it
Find what's slow. Log the request and upstream times, then sort the 499 lines by them:
log_format timing '$remote_addr "$request" $status '
'rt=$request_time urt=$upstream_response_time';
access_log /var/log/nginx/access.log timing;
If urt is close to some fixed number (10 s, 30 s, 60 s), a timeout in a client or load balancer is cutting the request off.
Line up the timeouts. Each layer should wait a little longer than the one behind it: client, then load balancer, then nginx (proxy_read_timeout, 60 s by default), then the app. Better still, make the endpoint fast: add caching or indexes, or move long work to a background job and return 202 with a status URL.
Let important work finish. By default nginx closes the upstream connection when the client leaves, which can abort the app's work halfway. For endpoints that must complete (payments, webhooks), set:
location /webhooks/ {
proxy_ignore_client_abort on;
proxy_pass http://app;
}
Make those handlers idempotent too, since the client will probably retry.
499 and SEO
Google never sees a 499. If Googlebot gives up on a slow page, nginx logs 499 and Google records a timeout or server error, and slows its crawl rate if that keeps happening. So 499 lines with a Googlebot user agent are a sign of pages that answer too slowly. Speed them up rather than raising timeouts.
Related codes
- 504 Gateway Timeout: nginx gave up on the upstream, the reverse case.
- 408 Request Timeout: the server gave up on a slow client.
- 502 Bad Gateway: the upstream failed or sent an invalid response.
- 524 A Timeout Occurred: Cloudflare's version of a slow origin.
To time how long a URL takes to answer, request it with the HTTP Request Builder.