Skip to content

HTTP 499 Client Closed Request: who gave up, and why

499 is not a reply a server sends. nginx, and services built on it, write 499 in their logs when the client closes the connection before the response is ready. If it is in your logs, a caller stopped waiting: its timeout is shorter than your response time, or something between you and it closed the connection. The fix is to make the response faster or to line up the timeouts along the route.

Reviewed by John Hale. Last updated .

What it looks like

In the nginx access log
203.0.113.7 - - [14/Nov/2026:10:12:03 +0000] "GET /report HTTP/1.1" 499 0 "-" "curl/8.7.1"

What the caller saw (curl 8.7.1)
curl: (28) Operation timed out after 2006 milliseconds with 0 bytes received

What the caller saw (Node.js 24 fetch)
TimeoutError: The operation was aborted due to timeout

What does 499 Client Closed Request mean?

499 is not part of HTTP. RFC 9110 does not define it, and the IANA registry lists 452 to 499 as unassigned. nginx defines it in its source as NGX_HTTP_CLIENT_CLOSED_REQUEST, for a client that closes the connection while nginx is still working on the request. Nothing is sent, because the connection is gone: 499 exists only in the log.

Other services use it the same way. Cloudflare records 499 in its logs and analytics when a client closes the connection before the request is complete, and sends no 499 response. AWS Application Load Balancers log the same event as 460. Google APIs are the exception that puts it in a reply: a cancelled call maps to HTTP 499.

The client is whatever opened the connection to that server, not always a person. Behind a load balancer or a CDN it is the load balancer or the CDN. When a request comes through a proxy, it is the proxy's exit address.

What causes a 499?

Most likely first.

  1. The caller's timeout is shorter than the response time. The most common cause. A script with --max-time 10, a timeout=10 in Python or an AbortSignal.timeout() in JavaScript closes the connection when the time is up, and the server logs 499 for a request it was still working on.
  2. A load balancer or CDN in front gave up first. When the layer in front of nginx has a shorter timeout than the application, it closes its connection to nginx and answers the visitor itself, usually with 504. nginx logs 499 and the visitor never sees that number.
  3. A person left. Closing the tab, pressing stop, clicking a link before the page finished or submitting a form twice all cancel requests that were in flight.
  4. The client cancels on purpose. Search boxes that cancel the previous request on each keystroke, clients that send the same request to two servers and drop the slower one, and health checks with a short deadline.
  5. A proxy or its exit dropped in the middle. When a request travels through a proxy, the server sees the proxy's exit as the client. If the exit goes offline or the proxy closes the tunnel, the server logs 499 although the program that sent the request was still waiting.

How do you find who closed the connection?

  1. Log how long each request ran

    $request_time is the time from the first bytes read from the client to the log write. $upstream_response_time is the time spent receiving the answer from the application. 499s whose request time piles up at a round number such as 10.000, 30.000 or 60.000 point to a timeout set by the caller or by a layer in front.

    nginx.conf
    log_format timed '$remote_addr [$time_local] "$request" $status $body_bytes_sent '
                     'rt=$request_time urt=$upstream_response_time "$http_user_agent"';
    access_log /var/log/nginx/access.log timed;

  2. See which URLs are abandoned

    In the default combined log format the status is the ninth field and the path the seventh. A few slow URLs with most of the 499s are a speed problem; 499s spread evenly over every URL point to the network or to a layer in front.

    Most abandoned paths
    awk '$9 == 499 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head

  3. Check who the client is

    Look at the address in the log line. If it is your load balancer or your CDN, the visitor did not close the connection: that layer did, and its own log has the status the visitor received.

  4. Reproduce it with a deadline

    Give curl less time than the URL needs. curl prints error 28 and status 000, and the server logs 499 for that request.

    Two seconds for a slow URL
    curl -sS -o /dev/null --max-time 2 -w "%{http_code} %{time_total}s\n" https://TARGET/slow

How do you fix 499s?

  • Answer faster, or answer early

    Speed up the slow URLs the log points to. For work that takes long by nature, accept the request, answer at once with a job ID and let the caller ask for the result.

  • Line up the timeouts

    Each layer should wait a little longer than the one behind it: the application's own limit shortest, then nginx's proxy_read_timeout, then the load balancer or CDN, and the caller longest. Then a slow request ends as a clear 504 from your side, not as a closed connection.

  • Let important work finish

    By default nginx closes its connection to the application when the client leaves. proxy_ignore_client_abort on keeps it open until the application answers. Use it for requests that must not be cut short, such as payments.

    nginx.conf
    location /payments/ {
        proxy_ignore_client_abort on;
        proxy_pass http://app;
    }

  • Make repeats safe

    The application may finish the work after the caller has gone. In our test a server that took 10 seconds completed and wrote its answer without any error, 8 seconds after the caller had closed the connection. Before a client retries a request that changes something, give the request an idempotency key so the second attempt does not do the work twice.

  • On the calling side

    Set a timeout that fits the slowest answer you expect, and retry requests that are safe to repeat with a growing pause. Through a proxy, allow for the longer route: a deadline that works on a direct connection can be too short through a residential exit.

If you use TrueProxies

The gateway never answers 499. When it refuses a connection itself, it answers 407, 403, 502 or 503, with the reason in X-Proxy-Reason where there is one.

When your client gives up, the tunnel closes and the website sees the proxy exit close its connection. A site that runs nginx logs that as 499 against the exit address. Traffic that moved before the close still counts as traffic used.

Usage history on the service page lists these connections under Ended on your side: Reset on your side, Timed out on your side and Left open after response. They are not proxy errors and do not count against connection success.

A residential exit can go offline in the middle of a connection. A sticky session then gets a new IP; keep the session ID and retry.

Test the proxy from outside your network

The free proxy checker connects from our servers and reports the failed stage as a code, so you can tell a proxy problem from one on your computer or network.

Run the proxy checker

Questions about 499 Client Closed Request

Is a 499 an error on my server?

Not by itself. It says the caller left before the answer was ready. It becomes your problem when the answer was slow enough to make callers leave, so look at the request times of the 499s.

What is the difference between 499 and 504?

With a 504 a gateway waited for the server behind it, gave up and told the client. With a 499 the client left first, so there was nobody to tell. The same slow request can be a 499 in the nginx log and a 504 from the load balancer in front of it.

What is the difference between 499 and 408?

A 408 Request Timeout is sent by a server that gave up waiting for the client to finish sending its request. A 499 is logged when the client gave up waiting for the response.

Can a client ever receive a 499?

Not from nginx or Cloudflare, which only log it. Some APIs do send it: Google APIs answer a cancelled call with HTTP 499.

Does the server stop working when the client leaves?

nginx closes its connection to the application unless proxy_ignore_client_abort is on. Whether the application stops depends on the application: many keep working until they try to write the answer, and some never notice.

Why do 499s rise when I scrape through proxies?

The route is longer, so answers take longer, and a deadline tuned for a direct connection starts to cut requests off. Each one shows as a timeout in your scraper and as a 499 in the site's log. Raise the deadline or run fewer requests at once.