HTTP status code · Unofficial, used by nginx
499 Client Closed Request
499 Client Closed Request is an nginx log code meaning the client closed the connection before nginx sent a response. The usual cause is a backend slower than the client’s own timeout: a load balancer, CDN, mobile app or impatient user gave up first.
| Class | 4xx/5xx, Unofficial codes (nginx, Cloudflare) |
|---|---|
| Defined in | nginx source: NGX_HTTP_CLIENT_CLOSED_REQUEST |
| Cacheable by default | No; the client is gone before anything is sent |
| Safe to retry | The client already gave up; a retry only helps if it waits longer or the backend gets faster |
| Relevant headers | None specific to this code |
What 499 means
The code is defined in the nginx source as NGX_HTTP_CLIENT_CLOSED_REQUEST and is never sent to anyone: by the time nginx would write it, the socket is already closed. It exists so that your access log explains a request that otherwise would look like it vanished.
A 499 points at a timeout mismatch more often than at a bug. If Cloudflare in front of your origin stops waiting after its 125-second proxy read timeout, or an application load balancer has a 60-second idle timeout, nginx behind it logs 499 for every request that takes longer, while the visitor sees a 524 or 504 from that front proxy.
By default nginx also closes the connection to the upstream when the client leaves (proxy_ignore_client_abort off), so the backend may be interrupted mid-work. Turn that directive on only when the upstream work must finish regardless, such as a payment callback.
Common causes
If you are visiting the site
- You closed the tab, pressed stop, or navigated away while the page was still loading.
- A flaky mobile connection dropped mid-request, or the app you use has a short request timeout.
If you run the server
- The upstream (PHP-FPM, Node, a database query) takes longer than the timeout of whatever sits in front of nginx: a CDN, a cloud load balancer or a client library default.
- Health checks or monitoring probes with a timeout of a few seconds hitting a slow endpoint.
- Users double-clicking or search-as-you-type code aborting the previous fetch with AbortController, which is harmless and shows up as short 499s.
How to fix it
If you are visiting the site
- Reload and wait for the page to finish; if it hangs again, the slowness is on the server side.
If you run the server
- Log $request_time and $upstream_response_time. 499s that cluster around one value (60 s, 100 s, 125 s) reveal the timeout of the component in front of nginx.
- Make the slow endpoint faster, or move the long work to a background job and return 202 Accepted with a status URL to poll.
- Order the timeouts from the inside out: backend shorter than nginx proxy_read_timeout, which is shorter than the load balancer or CDN, so failures surface as a clear 504 from nginx instead of a 499.
- Ignore short 499s from cancelled fetches in alerts; they are expected client behavior.
How to diagnose 499
You never send 499 yourself; nginx writes it to the access log when the client disconnects. The snippets show how to log it usefully and find the pattern.
# 499 lands in $status; log timings to see who gave up first
log_format timing '$remote_addr "$request" $status '
'rt=$request_time urt=$upstream_response_time';
access_log /var/log/nginx/access.log timing;
location /api/ {
proxy_pass http://app;
proxy_read_timeout 60s;
# Optional: let the upstream finish even if the client leaves
# proxy_ignore_client_abort on;
}# Which URLs get the most 499s (default combined log: status is field 9)
awk '$9 == 499 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head
# With the timing format above: how long did those requests run?
grep ' 499 rt=' /var/log/nginx/access.log | grep -o 'rt=[0-9.]*' | sort | uniq -c | sort -rn | headCommonly confused with
- 499 vs 408
- 408 is the server giving up on a client that is too slow to send its request; 499 is the client giving up on a server that is too slow to answer.
- 499 vs 504
- 504 is nginx itself timing out on the upstream and telling the client; 499 means the client left before nginx reached its own timeout.
- 499 vs 444
- 444 is nginx closing the connection on purpose; 499 is the other side closing it.
Frequently asked questions
- Is a 499 error a problem with my server?
- Often, yes, indirectly. A few short 499s are normal cancellations, but many 499s with long request times mean the backend is slower than the timeout of the client or proxy in front of nginx.
- Why do I see 499 in nginx but 524 or 504 in the browser?
- Something between the browser and nginx timed out first. Cloudflare shows 524 after its 125-second read timeout; a load balancer shows 504. That proxy then closes its connection to nginx, which nginx logs as 499.
- Does the backend keep running after a 499?
- By default nginx closes the upstream connection too, but most application servers do not notice until they try to write, so the work usually runs to completion anyway. Design long operations to be idempotent or move them to a queue.
- Should I turn on proxy_ignore_client_abort?
- Only for endpoints whose work must finish even if the caller leaves. It keeps the upstream connection open after the client disconnects; it does not make the response reach anyone.
Last reviewed by Arielton Oberek.