Views: 23

499 Client Closed Request: Who Hung Up First (nginx)

499 Client Closed Request means the caller quit before nginx replied. Tell a real user from a load balancer that timed out. Free instant check, no sign-up.

Check your domain for this issue now

Free, no sign-up. Runs the exact check this guide describes and shows what to fix.

Problem

Your nginx access log is filling with 499 status codes. No user has reported an error page — because there isn’t one. 499 is nginx recording that the client closed the connection before nginx finished responding. The request came in, work started, and the other end walked away before the answer was ready.

Symptoms

  • nginx access logs show a final status of 499 on requests that otherwise look normal.
  • Nobody sees an error. There is no 499 page, because the connection was already closed when nginx tried to reply.
  • The 499s cluster on your slowest endpoints — reports, exports, search, anything that hits a database or an upstream API.
  • An HTTP Check or curl against the same URL from a patient client returns a clean 200. The failure only shows up under a timeout.

What 499 Actually Means

499 is not a standard HTTP status code. nginx invented it, and it lives only in nginx’s own logs. When the client closes the TCP connection before nginx has sent its response, nginx has nothing to reply to, so it writes 499 to the access log and moves on. That status line is never transmitted — no browser, proxy, or CDN downstream ever receives a 499. This is the single most misread fact about it: 499 is a record that the caller left, not a response nginx sent to anyone.

So the question 499 is really asking is: who hung up, and why were they in a hurry?

The tempting answer is “an impatient user closed the tab.” Sometimes true. But in a modern stack, nginx almost never faces a browser directly. It sits behind a load balancer, a CDN, an API gateway, an ingress controller — and those are the clients that show up in the 499. Each of them enforces its own timeout, and the shortest one wins. If your AWS ALB has a 60-second idle timeout and your upstream takes 75 seconds to build a report, the ALB gives up at 60, closes its connection to nginx, and nginx logs a 499 for a request that was seconds from succeeding. The “client” in “Client Closed Request” was your own infrastructure.

That reframes the whole investigation. A spike of 499s is rarely a client problem. It’s a timeout-mismatch problem: something upstream of nginx is slow, and something in front of nginx runs out of patience before the slow thing finishes.

The 499 That Isn’t Yours (460, 463, and Friends)

If nginx sits behind AWS, you’ll see the same event wearing a different number. An Application Load Balancer logs 460 when the client closed the connection before the ALB got a response from its target — the ALB’s version of 499. It logs 463 when the incoming X-Forwarded-For header carries more than 30 IP addresses (a hard limit no setting changes), which usually means a misconfigured chain of proxies stacking hops. HAProxy records the same client-abort story in its termination-state field. Different names, one story: a layer between the visitor and your application decided to stop waiting.

Don’t confuse 499 with nginx’s return 444 either. 444 is nginx deliberately closing a connection with no response — something you configured, usually to drop bad bots. 499 is the client closing on you. One is your choice; the other is someone else’s.

Top 3 Causes

  1. A fronting timeout shorter than your upstream response time - The most common cause by far. A load balancer, CDN, or gateway with a 30–60s timeout sits in front of an endpoint that occasionally takes longer. The front layer hangs up, nginx logs a 499, and the work was often nearly done. The fix is aligning timeouts up the whole chain, not touching nginx.
  2. A genuinely slow upstream - nginx is proxying to an app server, and that app is slow on certain routes — an N+1 query, an external API with no timeout, a cold cache. The client (human or machine) reasonably gives up. Here the 499 is a symptom; the disease is the upstream latency, and that’s what to profile.
  3. Health checks and abandoned requests - Monitoring probes, uptime checkers, and users navigating away all close connections early by design. A load balancer health check that expects a fast reply from a slow path will generate steady 499s that mean nothing. Separate these from real traffic before you treat the number as an incident.

Diagnose with DechoNet

  • HTTP Check the affected URL. If a normal request from our side returns a clean 200 in reasonable time, nginx and your upstream are healthy — the 499s are being generated by a client-side timeout, not a broken endpoint. That split is the whole diagnosis: it tells you the fix lives in a timeout setting or upstream latency, not in nginx’s config.
  • If our check is also slow or fails, you’ve confirmed the upstream itself is the problem, and the 499 is just how impatient callers report it.

Resolution Checklist

  • Confirm the endpoint is actually healthy. Run an HTTP Check and a curl -w '%{time_total}' from a patient client. A fast, clean 200 means nginx is fine and the 499 is about timing, not correctness.
  • Find every timeout in the chain and line them up. CDN, load balancer (idle_timeout on an ALB defaults to 60s), API gateway, nginx proxy_read_timeout, and the app server. The shortest wins — make the fronting timeouts longer than your realistic worst-case response, or make the response faster.
  • Profile the slow upstream. If 499s track specific routes, the real work is there: slow queries, an un-timed external call, missing caching. Fixing latency kills the 499s at the source.
  • Separate probes from people. Tag or exclude health-check and monitoring user agents so they don’t inflate the count. A 499 from an uptime robot is not an outage.
  • Decide whether the upstream should keep working after a disconnect. proxy_ignore_client_abort on tells nginx to finish the upstream request even after the client leaves — useful when the backend must complete (a payment, a write) but wasteful if it just burns resources on abandoned work. Default is off; choose deliberately.

When to Escalate

  • If 499s spike alongside upstream latency and real users are affected, this is a performance incident, not a logging quirk — escalate to whoever owns the slow service.
  • If the 499s trace to a load balancer or CDN timeout that’s shorter than your endpoints need, escalate to whoever owns that edge layer. The fix is a timeout policy, and it lives one hop upstream of nginx.

Related Tools

Related Guides

Share this guide

[Ad] Guide Detail Inline
← Back to All Guides