408 Request Timeout: Fix a Slow or Stalled Request
408 Request Timeout: the server quit waiting for the request, not the reply. Separate a slow client from a reaped keepalive. 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
A request returns 408 Request Timeout, or a connection dies mid-request and something in the chain logs a 408. The server received part of the request but gave up before the whole thing arrived.
Symptoms
- HTTP Check shows a final status code of 408, or the connection resets partway through.
- It happens on large uploads, slow mobile connections, or requests with big bodies — and rarely on small GETs.
- Your nginx or load-balancer logs are full of 408s that no user seems to have reported.
- The same endpoint works fine from a fast network and fails from a flaky one.
What 408 Actually Means
RFC 9110 (§15.5.9) defines 408 as the server deciding “to close the connection because it did not receive a complete request message within the time that it was prepared to wait.” Read that twice, because the direction is the whole game. 408 is about the request being slow to arrive — not the response being slow to leave.
That’s the opposite of the timeout everyone assumes. A 504 Gateway Timeout fires when a proxy waited too long for the upstream to answer. A 524 (Cloudflare) is the same idea at the edge. But a 408 fires when the server waited too long for you — the bytes of your request line, headers, or body came in too slowly, or stopped coming. The problem is on the sending side of the wire, or in the network between you and the server, not in the application logic behind it.
There’s a second twist that trips up half the people who see 408 in a log. Take nginx: when client_header_timeout or client_body_timeout elapses, nginx records a 408 in its access log and then simply closes the TCP connection without writing any status line. The browser, proxy, or CDN in front of it just sees a connection that went quiet and hung up. Nobody rendered a 408 page. So “we have thousands of 408s” frequently means “thousands of idle keepalive connections timed out and got reaped,” which is normal housekeeping, not an outage.
Top 3 Causes
- A reaped idle keepalive connection - The client opened a keepalive connection, sat idle past
keepalive_timeout, and the server closed it. If the client then tries to reuse that dead socket for a new request, or the server logs the reap as a 408, you get a 408 that represents nothing a user experienced. This is the single most common source of “phantom” 408s in nginx logs. - A genuinely slow client or upload - A big file over a weak mobile link, a stalled multipart upload, or a client that opened the connection and then dawdled before sending headers. The request body trickles in slower than
client_body_timeoutallows, so the server abandons it. Real users on bad networks hit this. - An intermediary idle timeout, mislabeled - Load balancers and CDNs enforce their own idle timeouts (an AWS ALB defaults to 60 seconds). When the connection is dropped for idleness, the layer that notices may surface it as a 408 even though no application logic ran. Long-polling and slow-streaming requests are the usual victims.
Diagnose with DechoNet
- HTTP Check to confirm whether the endpoint actually returns a 408 to a normal, fast request, or whether it responds cleanly. If a quick automated request from our side succeeds while your users see 408, the timeout is tied to their upload speed or network path — not to a broken server. That single split tells you which end to fix.
Resolution Checklist
- Reproduce it. Run an HTTP Check and a
curl -vfrom a fast connection. A clean 200 here means the server is healthy and the 408 is client- or network-specific. - Separate phantom 408s from real ones. If the 408s only live in your nginx access log and no user reports a broken page, they’re almost certainly reaped keepalives — tune, don’t panic.
- Look at what requests fail. If it’s uploads and large bodies, the client body is arriving too slowly; raise
client_body_timeout(nginx) or the equivalent, and check upload-size limits aren’t compounding it. - Check every timeout in the chain, not just the origin.
keepalive_timeoutandclient_header_timeouton nginx, plus the idle timeout on any load balancer or CDN in front. The shortest one wins. - If you retry, retry safely. A 408 is fine to re-send because nothing was processed, but confirm the request is idempotent before wiring automatic retries into a payment or write path.
- Rule out 504 vs 408. If the delay is the server taking too long to answer, you’re chasing a 504, and the fix lives in the application or upstream — not in client timeouts.
When to Escalate
- If real users on normal connections get 408s on small requests, escalate to whoever owns the reverse proxy or load balancer — an idle timeout is set too aggressively for your traffic pattern.
- If 408s spike alongside a traffic surge, investigate a Slowloris-style pattern: many connections opened and fed bytes just slowly enough to hold sockets open. That’s a denial-of-service shape, not a config bug, and it belongs with whoever runs your edge defenses.
Related Tools
Related Guides
Share this guide