pingrobot

HTTP status codes / 408

408 Request Timeout — HTTP Status Code

· 3 min read

Short answer

408 Request Timeout means the server stopped waiting because the client did not finish sending its request in time. It is about the request arriving too slowly, not the server taking too long to answer. Clients may retry the request, usually on a new connection.

What 408 means

RFC 9110 (Section 15.5.9) defines 408 Request Timeout as the server not receiving a complete request message within the time it was prepared to wait. The RFC allows the client to repeat the request, using a new connection if the current one is no longer usable.

The direction is what trips people up. A 408 is the server timing out on the client. The request headers or body arrived too slowly, or not at all. It has nothing to do with how long your application takes to generate a response.

When you see it

  • nginx sends 408 when a client does not deliver the request headers within client_header_timeout, or pauses longer than client_body_timeout between two reads of the body. Both default to 60 seconds.
  • Apache with mod_reqtimeout sends 408 when headers or body arrive too slowly, which also protects against slow-sending attacks.
  • Load balancers such as AWS Application Load Balancers can return 408 when the client sends nothing before the idle timeout.
  • Application servers. Node.js's built-in HTTP server enforces its own headersTimeout and requestTimeout and responds with 408 when they expire.
  • Idle keep-alive connections. Some servers send a 408 just before closing a connection that sat idle, and browsers generally retry quietly.

Common causes

  1. Large uploads on slow or unstable connections, especially mobile networks.
  2. Clients that open a connection and send the body late, such as a script that builds the body while the socket is already open.
  3. Mismatched timeouts across layers. A CDN or load balancer happily holds a slow upload while the origin's shorter timeout fires first.
  4. Slow-sending attacks that hold connections open by trickling bytes. Here the 408 is the server defending itself.
  5. Client bugs where the declared Content-Length is larger than the body actually sent, so the server waits for bytes that never come.

How to fix it

Decide first whether the timeouts are protecting you or getting in the way of legitimate traffic.

If real users upload large files over slow links, raise the body timeout for the upload path only, rather than globally:

location /upload {
    client_body_timeout 120s;
    client_max_body_size 200m;
    proxy_pass http://app;
}

Remember that client_body_timeout applies between two successive reads, not to the whole upload, so a slow but steady upload is usually fine. Long silences are what trigger it.

In Apache, tune mod_reqtimeout with a minimum data rate so slow but active clients keep going:

RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500

On the client side, make sure Content-Length matches the body you send, prefer chunked or resumable uploads for big files, and retry idempotent requests on a fresh connection.

How to check it

You can reproduce a 408 by opening a connection and not finishing the request. This sends a partial request line and then waits:

(printf 'GET / HTTP/1.1\r\nHost: example.com\r\n'; sleep 90) | \
  openssl s_client -quiet -connect example.com:443 2>/dev/null | head -n 1

If the server's header timeout is shorter than 90 seconds, you will see HTTP/1.1 408 or the connection simply closes; some servers close without sending a status. For everyday checks, curl's timing output shows where time goes:

curl -sS -o /dev/null -w "%{http_code} connect=%{time_connect} total=%{time_total}\n" https://example.com/

What it means for uptime monitoring

PingRobot's checker sends a small GET with the user agent PingRobot/1.0 (+https://pingrobot.io/bot) and no body, follows redirects, and counts the final response as up only if its status is 200 to 399. A 408 would be reported as down.

Because the checker's request is tiny and sent in one go, a 408 in response to it is unusual. If you do see one, it suggests the server or a load balancer in front of it is struggling to even read requests, for example because worker connections are exhausted, or that a timeout is configured far too aggressively. Both affect real visitors, so treat repeated 408s as a genuine problem. A single 408 that the second-region re-check does not confirm is more likely a network blip.

Do not confuse it with the checker's own timeout: if the server never responds at all, PingRobot records a timeout, not a 408.

Start monitoring free — 10 monitors, no credit card.

Frequently asked questions

What is the difference between 408 and 504?

408 means the server waited too long for the client to send the request. 504 Gateway Timeout means a proxy waited too long for an upstream server to send the response. One is the client being slow, the other is the backend.

Can a slow server cause a 408?

Not directly. The timer runs while the server reads the incoming request. A slow app produces 504 at a proxy, or a client-side timeout, not a 408.

Is it safe to retry after a 408?

RFC 9110 says the client may repeat the request, and for a GET that is harmless. For a POST that creates something, retry only if the API is designed for it, for example with an idempotency key.