A 408 Request Timeout means the server got tired of waiting for you - not the other way around. Your browser or app opened a connection, started sending a request, and then took too long finishing it. The server closed the connection and handed back a 408. It looks like a crash, but it usually isn't one.
What 408 Actually Means (and What It Doesn't)
Most hosting errors are the server struggling to respond. 408 is the opposite: the server is waiting on the client - the browser, an app, a script, a webhook sender - to finish sending its request headers or body, and that client is too slow. Once the server's patience (its timeout window) runs out, it drops the connection and returns 408 instead of hanging forever.
It's easy to lump this in with its cousins, but they're different problems:
- 408 Request Timeout - the client was too slow sending the request.
- 504 Gateway Timeout - the backend (PHP-FPM, an app server) was too slow sending the response.
- 503 Service Unavailable - the server is overloaded and refusing new work entirely.
If you fix 408 by tuning PHP's max_execution_time, nothing changes - that setting controls how long your script runs, not how long the server waits for the request to arrive. That's the single most common wasted troubleshooting hour on this error.
Symptom: Where You'll Actually See It
408 shows up in a handful of predictable places:
- Uploading a large file through WordPress Media Library, cPanel File Manager, or an FTP-over-HTTP form.
- Submitting a long form (a big WooCommerce checkout, a multi-field application form) over a slow or unstable connection.
- Mobile visitors on patchy 3G/4G, hotel wifi, or a flaky VPN.
- API clients or webhook senders that open a connection early and pause before writing the body.
- Intermittently, and almost never for the same visitor twice in a row on a fast connection - that pattern alone is a strong hint it's a client-speed issue, not a broken script.
Cause: Why the Server Gives Up on You
The timeout window is too strict for the request size
Every web server sets a limit on how long it will wait for a request body to finish arriving. Nginx calls this client_body_timeout; Apache calls it Timeout. Defaults (often 60 seconds) are fine for normal form posts but too short for a 200MB video upload crawling over a slow connection.
An actually slow or unstable connection
If the visitor's upload speed is 200KB/s and they're pushing a 50MB file, simple math says that takes over 4 minutes - well past most default timeouts. Wifi drops and mobile handoffs make it worse by stalling mid-transfer.
A reverse proxy or CDN in the chain has its own clock
If Cloudflare (or any proxy) sits in front of your origin, it enforces its own timeout independent of your server's. On Cloudflare's Free and Pro plans, that's a fixed 100 seconds for the edge-to-visitor leg - a large upload over a slow connection can hit that ceiling before it hits your Nginx or Apache setting.
A security layer is holding the connection open
ModSecurity, Imunify360, or an aggressive WAF rule can inspect a request body before passing it along, adding delay on top of the raw transfer time. On an already-slow connection, that extra buffering is sometimes enough to tip it over the timeout.
The Fix
On Nginx (VPS with Nginx or Nginx-as-proxy)
Raise the client-side timeouts in your server block, then test and reload:
server {
client_body_timeout 300s;
client_header_timeout 60s;
keepalive_timeout 75s;
client_max_body_size 256M;
}
nginx -t && systemctl reload nginxDon't set client_body_timeout to something absurd like 3600s "just in case" - a very long window makes your server more exposed to slow, connection-holding attacks (Slowloris-style). A few minutes is usually enough headroom for legitimate large uploads.
On Apache (cPanel/WHM VPS with root access)
The Timeout directive can't be set in .htaccess - it has to go in the main config or, on a cPanel server, through WHM > Service Configuration > Apache Configuration > Global Configuration, where there's a Timeout field. Bump it to 300 seconds, save, and Apache rebuilds and restarts automatically.
On shared cPanel hosting (no root access)
You can't edit server-level timeout values yourself here - that's a hosting-provider setting. Open a support ticket and ask specifically for the Apache/Nginx Timeout/client_body_timeout to be raised for your account, and mention the file size and rough upload speed you're dealing with so support can pick a sane number instead of guessing.
Stop hitting the limit in the first place - chunked uploads
For WordPress specifically, the cleanest fix isn't a bigger timeout, it's avoiding one giant request. A plugin like Tuxedo Big File Uploads (built on resumable.js) splits large media uploads into small chunks sent as separate requests, so no single request runs long enough to time out - and a dropped wifi connection just resumes instead of failing the whole upload.
If Cloudflare is in the path
For an upload endpoint that regularly handles large files (a media import tool, a custom upload form), consider a Page Rule that sets that specific path to DNS-only (grey cloud) so the upload goes straight to your origin without Cloudflare's edge timeout in the middle. Keep the rest of the site proxied for the CDN and security benefits.
Prevention
- Match your web server's body timeout to realistic upload sizes for your site, not the default.
- Use chunked/resumable uploads for anything over ~50MB instead of relying on one long request.
- Keep
client_max_body_size(Nginx) orLimitRequestBody(Apache) in sync with your PHPupload_max_filesizeandpost_max_size- a mismatch just moves the failure from one error to another. - Watch your error logs for repeated 408s from the same IP in a short window - that's a different problem (a bot or scanner holding connections open) and worth a firewall rule, not a longer timeout.
| Layer | Setting | Typical default | Where to change it |
|---|---|---|---|
| Nginx | client_body_timeout | 60s | Server block, then nginx -t && systemctl reload nginx |
| Apache | Timeout | 60s | WHM > Apache Configuration > Global Configuration |
| Cloudflare (proxied) | Edge timeout | 100s (Free/Pro) | Page Rule to DNS-only for the upload path, or Enterprise plan for a longer limit |
| WordPress | Request size | N/A | Chunked-upload plugin instead of one large request |
408 isn't a sign your hosting is broken - it's the server correctly refusing to wait forever on a slow client. Match the timeout to the request, or split the request into pieces small enough that timing out stops being a risk.