Scrapeless Wiki

HTTP 408 Request Timeout Explained – Causes and Fixes

Troubleshooting P3 http 408 request timeout

Learn what HTTP 408 means, how it differs from a socket timeout, and why it usually points at the client or an idle connection.

HTTP 408 means the server gave up waiting for the client to finish sending its request. The server is telling you it timed out on you β€” which makes it different from every other timeout you will meet.

1. 408 Versus a Socket Timeout

HTTP 408 ETIMEDOUT
Origin The server, as a response The operating system
Meaning Your request was too slow to arrive No response arrived in time
Direction Server waiting on client Client waiting on server
Body A real HTTP response None

Receiving 408 proves the connection worked and the server was listening. That already rules out most network diagnoses.

2. The Most Common Cause Is Not an Error

Servers send 408 to close idle keep-alive connections. A connection sits unused past the server's idle timeout, the server closes it, and a 408 is the goodbye.

This is entirely normal and is not a problem with your request. If it appears on the first request over a reused connection after a pause, this is almost certainly what happened.

The correct handling is to retry once on a fresh connection, which every mature HTTP client does automatically.

3. Genuine Causes

A slow request body. A large upload on a slow link can exceed the server's read timeout for the request.

An incomplete body. A Content-Length larger than the bytes actually sent leaves the server waiting for data that never arrives, until it times out.

A stalled client. A process that opens a connection, writes headers, and then blocks before finishing the body.

Buffering intermediaries. A proxy that buffers a request slowly enough to trip the origin's timeout.

The Content-Length mismatch is the one worth checking first in your own code, because it is a bug rather than a condition.

4. Retrying

408 is explicitly retryable, and safely so β€” the server is stating it did not process the request.

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import requests

session = requests.Session()
session.mount("https://", HTTPAdapter(max_retries=Retry(
    total=3, backoff_factor=0.5,
    status_forcelist=[408, 429, 500, 502, 503, 504],
    allowed_methods=frozenset(["GET", "HEAD", "POST", "PUT", "DELETE"]),
)))

Including 408 in a retry list is uncontroversial in a way that 500 or 502 is not, because the semantics guarantee the request had no effect.

5. When 408 Is Really Something Else

Some defences and edge networks return 408 for reasons unrelated to timing β€” as a generic rejection, or in place of a rate-limit response. The tells:

  • It appears immediately, with no delay before the response.
  • It correlates with request rate rather than request size.
  • Retrying on a fresh connection changes nothing.
  • A different egress IP succeeds instantly.

In that case treat it as throttling: reduce concurrency, add delay, and stop retrying harder β€” the same response a 429 deserves.

6. Fix Checklist

  1. Confirm whether it is keep-alive expiry β€” first request after an idle period, retry succeeds. Nothing to fix.
  2. Check Content-Length matches the bytes you send.
  3. Verify the body is fully written and flushed before waiting for a response.
  4. Retry once on a new connection, which resolves the common case.
  5. Reduce keep-alive idle time in your client so it closes before the server does.
  6. If immediate and rate-correlated, treat it as rate limiting and slow down.
  7. For large uploads, check whether chunked encoding avoids the timeout.

7. Summary

HTTP 408 is the server reporting that it stopped waiting for your request, and it most often means a keep-alive connection expired rather than that anything failed.

It is always safe to retry because the request was not processed β€” so retry once on a fresh connection, check Content-Length against what you actually send, and when the 408 is immediate and tracks request rate, read it as throttling and back off instead.