A large upload stalls, an API client retries, and the only visible clue is a 408 response code. Raising every timeout may hide the symptom, but it can also keep abandoned connections open longer and leave the real failure untouched.
The 408 response code means a server did not receive the complete HTTP request within the time it was willing to wait. Diagnose it by finding which layer returned the response and whether the request stalled in its headers or body; then fix that layer's receive timeout, network path, or upload design rather than tuning backend processing time.
What the 408 response code actually means
HTTP 408 is named Request Timeout and sits in the 4xx client-error class. The current HTTP specification defines it narrowly: the server did not receive a complete request message before its waiting period ended. If the connection is no longer usable, a retry uses a new connection (RFC 9110, section 15.5.9).
The important word is receive. A 408 normally occurs while a server, reverse proxy, load balancer, or CDN is still waiting for the client's request headers or body. The application might never receive a complete request to process.
A browser can also open a connection speculatively and never use it. MDN notes that some servers send a 408 on an idle connection, sometimes before the user sends a request at all. These entries can be harmless log noise when they do not correspond to a failed user action.
Do not read “4xx” as proof that the user is at fault. A slow client or unstable route can trigger the timeout, but so can a receive window that is too short for legitimate uploads, mismatched timeouts between layers, or an overloaded intermediary that stops reading promptly.
408 vs 499, 504, and other timeout signals
“Timed out” describes an outcome, not the failed hop. The status and the component that generated it tell you which direction to investigate.
| Signal | What timed out | First place to look |
|---|---|---|
| 408 Request Timeout | A server-side layer waited for the client to finish sending the request | Client upload, request headers/body, edge or origin receive timeout |
| 499 Client Closed Request | The client closed the connection before the server could respond; 499 is a non-standard code used by some servers | Client deadline, navigation/cancellation, downstream latency |
| 504 Gateway Timeout | A gateway or proxy waited too long for an upstream response | Origin health, application work, database or upstream dependency |
| 524 timeout | Cloudflare connected to the origin but did not receive a timely HTTP response | Origin processing and proxy read timeout |
MDN's 504 definition makes the boundary clear: a 504 is generated by a gateway that did not receive an upstream response in time. A 408 points in the opposite direction—the responding layer did not receive the full request from its client.
That distinction prevents a common wrong turn. Increasing an application handler's execution timeout will not repair a request body that stopped arriving before the handler ran. Likewise, tuning client-body receive limits will not fix an origin that accepts a request and then spends too long waiting on a database.
Why a 408 Request Timeout happens
Most incidents fall into one of five patterns.
The client sends too slowly or stops mid-request
Mobile handoffs, packet loss, congested Wi-Fi, VPNs, and corporate proxies can pause an upload long enough to cross a server's receive window. The failure is more visible with large form submissions, video uploads, and API bodies because they spend longer in transit.
The total upload duration is not always the timer being measured. NGINX, for example, applies client_body_timeout between successive read operations rather than across the entire body. Its default is 60 seconds, and a period with no new body data triggers a 408 (NGINX HTTP core module).
Request headers never finish
A client can connect but fail to complete the request line and headers. Causes include a broken client implementation, slow transmission, a stalled TLS path, or an intermediary that forwards only part of a request.
NGINX has a separate client_header_timeout; Apache's mod_reqtimeout can set independent handshake, header, and body budgets. Apache documents that exceeding any configured request-read stage produces a 408.
Timeout budgets disagree between layers
A production request may cross a CDN, web application firewall, load balancer, ingress proxy, web server, and application. Each layer can enforce a receive or idle limit. The first timer to expire owns the visible error, so changing a different layer has no effect.
For example, AWS documents that an Application Load Balancer can return 408 when a client sends no data before the idle timeout expires. A TCP keepalive alone does not reset that HTTP data timer.
The intermediary or server is under pressure
CPU starvation, connection exhaustion, disk pressure from buffered request bodies, or an overloaded inspection layer can delay reads. From the client's perspective the upload is active, but the receiving process is not consuming bytes quickly enough. Shorter timeouts then turn resource pressure into more 408 responses.
Idle and preconnected sockets create harmless 408s
Browsers and clients reuse persistent connections and may open connections in anticipation of a request. A server that retires one of those idle sockets can log a 408 even though no page load failed. Separate these connection-management events from 408s tied to real request IDs, upload routes, or user reports before declaring an incident.
How to diagnose a 408 response code
Start with one failing request and build a timeline. Aggregate graphs can show scope, but they rarely reveal which hop produced the response.
- Capture the failure at the client. Record timestamp, method, URL, payload size, elapsed time, response headers, response body, and any request or trace ID. Note whether failure time clusters around a fixed boundary such as 20 or 60 seconds.
- Identify the responding layer. Compare CDN, load balancer, proxy, and origin logs for the same request. Branded error bodies and headers can suggest a source, but logs are stronger evidence.
- Determine whether the request completed. Look for bytes received, declared
Content-Length, upload duration, header-read duration, and whether an application trace began. No application entry plus an edge 408 usually points to receipt before the app. - Find the timeout that matches the elapsed time. Inventory client send deadlines, edge idle limits, load-balancer idle timeouts, proxy header/body read settings, and web-server request-read settings. The observed boundary often identifies the owner.
- Segment the failures. Compare route, method, payload size, client version, ASN, geography, HTTP version, and edge location. Upload-only or network-specific concentration is more useful than a global average.
- Reproduce one variable at a time. Try a small body, then the original body; a clean connection, then connection reuse; a direct controlled origin path, then the normal edge path. Use only authorized test environments when bypassing production controls.
A packet capture can prove that the client stopped sending or that a peer closed the connection, but begin with existing access logs and traces. Packet-level data is harder to collect safely and may contain credentials or personal data.
When a CDN is present, decide whether it generated the response or proxied it. Cloudflare's 408 guidance says most 408s on its network are proxied from the origin, although Cloudflare can also enforce its own non-configurable client-request timeouts. Other providers have different policies, so verify rather than generalize.

How to fix a 408 response code at the right layer
Choose the change that matches the evidence. Do not start by setting every timeout to a large number.
For users and API consumers
- Retry once on a stable connection when the operation is safe to repeat.
- For a large upload, avoid switching networks or letting the device sleep mid-transfer.
- Update the browser, application, SDK, or proxy if failures correlate with one client version.
- Check that the request body length and transfer encoding are correct. A client that declares more bytes than it sends can leave the receiver waiting for data that will never arrive.
- Use resumable or multipart uploads for large objects so a brief interruption does not restart the entire transfer.
Opening a fresh connection matters because an HTTP/1.1 connection associated with an incomplete request can no longer have a reliable message boundary. The 408 specification explicitly allows repeating an outstanding request on a new connection.
For NGINX and Apache operators
On NGINX, inspect both client_header_timeout and client_body_timeout. The documented defaults are 60 seconds, but inherited configuration may differ. Raise a value only when legitimate clients demonstrably need a wider quiet interval, and keep an upper bound that limits abandoned or intentionally slow connections.
On Apache, inspect RequestReadTimeout from mod_reqtimeout. Its minimum-rate options can grant more time while a client continues to make progress, which is usually safer than one unlimited window. Apache also documents LogLevel reqtimeout:info for exposing these timeout events in logs.
After a change, reload configuration safely, run a syntax check, and test slow headers and bodies in a non-production environment. Watch connection count, memory, buffered-body storage, and request-read duration; a wider timeout consumes resources for longer.
For load balancers, proxies, and CDNs
Create a table of timeout ownership rather than trying to force one identical value everywhere:
| Layer | Timer to verify | Desired relationship |
|---|---|---|
| Client | Send and total request deadline | Long enough for the supported payload and network conditions |
| CDN/load balancer | Client receive or idle timeout | Long enough to accept legitimate progress; bounded against idle abuse |
| Reverse proxy/web server | Header and body read timeouts | Consistent with edge behavior and upload policy |
| Application/upstream | Processing and response timeout | Diagnosed separately from request receipt |
One structural fix is worth considering alongside the timeout table. A receiver that is starved of CPU or connection capacity produces 408s that no timer change will repair, and on most origins the traffic consuming that capacity is cacheable reads rather than the uploads that are failing. Moving that read traffic off the origin gives the ingress path headroom to consume request bodies promptly — ZeroBuffer serves it from the edge with origin shield and instant purge, on a flat $0.0049/GB rate with no regional tiers to model. It is a capacity fix, not a timeout fix: the receive budgets themselves still belong to the ingress layer you identified above. See how CDN caching changes the delivery path.
For large uploads
Treat repeated 408s as a design signal, not just a timer problem.
- Split data into independently retryable parts and persist upload progress.
- Validate size, type, authentication, and quota before transferring a large body where the protocol permits.
- Measure per-part throughput and failure rate so slow networks are visible.
- Avoid buffering the entire body at multiple intermediaries unless inspection or application semantics require it.
- Set documented payload limits and return a specific error such as 413 when the request is too large; do not let an unsupported upload drift into a timeout.
Retry a 408 safely
A retry may succeed if the original request never arrived completely, but the status alone does not prove that no side effect occurred. A proxy could generate the 408 while another layer has already seen enough data to act, or a client might misclassify a connection failure.
Automatically retry safe methods such as GET on a fresh connection with a small, bounded delay. For POST and other operations that may create side effects, retry only when the API provides an idempotency mechanism or the client can verify the original operation was not applied. RFC 9110's idempotency guidance says clients should not automatically retry a non-idempotent request without such knowledge.
Set a maximum attempt count and a total deadline. Retrying indefinitely can turn a localized network problem into duplicated uploads and unnecessary connection pressure. This is different from handling a 429 status code, where the server is explicitly asking the caller to reduce request rate.
Prevent the next 408 incident
Prevention means making request receipt observable and giving supported clients enough time without leaving connections unbounded.
- Track 408 count and rate by the layer that generated it, not only at the origin.
- Log request IDs, method, route, protocol, bytes received, expected bytes, request-read duration, edge location, and timeout reason where supported.
- Alert on user-impacting ratios and sustained changes, not isolated speculative connections.
- Test uploads under controlled latency, bandwidth limits, packet loss, and connection reuse before releases.
- Publish client-side payload, timeout, retry, and idempotency expectations.
- Review timeouts whenever a new CDN, WAF, load balancer, ingress controller, or proxy is inserted into the path.
- Correlate 408s with saturation metrics to detect a receiver that has stopped consuming data promptly.
Add the result to your broader application availability telemetry. A low volume of idle-connection 408s can be normal; a rising failure ratio on a critical upload route is an availability problem even if the origin's CPU chart looks healthy.
Frequently asked questions
What does a 408 response code mean?
It means the responding server-side layer did not receive the complete request message within its configured waiting period. The missing portion may be request headers or body data, and the response can come from an edge, load balancer, proxy, or origin server.
Is a 408 error client-side or server-side?
HTTP classifies 408 as a 4xx client error, but either side can contribute. Slow transmission and incomplete bodies are client-path problems; short receive windows, mismatched proxy settings, or a receiver under resource pressure are server-side or infrastructure factors.
What is the difference between 408 and 504?
A 408 means a server did not receive the client's complete request in time. A 504 means a gateway or proxy received the request but did not receive a timely response from an upstream server.
Can a CDN cause a 408 response code?
Yes. A CDN can generate a 408 when the client does not complete a request within its ingress timeout, or it can pass through a 408 generated by an origin. Compare edge and origin logs for the same request ID before changing either configuration.
Should I increase the server timeout to fix 408 errors?
Only when measurements show legitimate clients are making progress but exceed the current header or body receive window. Raising timeouts without locating the failed stage can retain abandoned connections longer and will not fix application processing or upstream-response delays.
Is it safe to retry after a 408?
Retry safe or otherwise idempotent operations on a new connection with a bounded attempt budget. Do not automatically retry a potentially state-changing request unless an idempotency key or an application check prevents duplicate effects.
Fix the failed hop, not every timer
A 408 becomes tractable once you separate request receipt from response generation. Capture one failure, identify the responding layer, prove whether headers or body data stopped arriving, and match the elapsed boundary to the timeout that owns it.
Then make the smallest defensible change: repair the client or path, align the relevant receive budget, redesign fragile uploads, or relieve a receiver that is not consuming data. Use the next incident review to document timeout ownership across the full delivery chain so the same status code leads directly to the right team and the right evidence.
Timeout ownership is easiest to document when the delivery layer is one you can actually inspect. ZeroBuffer publishes its edge behavior rather than hiding it behind an opaque proxy tier: origin shielding that collapses cache-miss fan-out before it reaches your origin, anycast routing that withdraws an unhealthy or congested location so traffic reroutes to the next-nearest edge, configurable cache and TTL policies, and instant purge across 100+ edge locations — at a flat $0.0049/GB with no contracts or minimums. See how the delivery stack is put together.
