A 504 Gateway Timeout means the proxy waited for the backend and gave up. The backend is often still working, just too slowly. The fix is usually to make that request faster, and only sometimes to raise a timeout.
Before you start
- Root access to the VPS and the URL and time of the error.
- Know your chain: browser → (CDN) → Nginx → PHP-FPM, or Nginx → Node/Python app server.
Confirm where the timeout happens
Nginx logs it in /var/log/nginx/error.log:
upstream timed out (110: Connection timed out) while reading response header from upstreamIf Nginx shows nothing but users see a Cloudflare-branded 504/524 page, the CDN timed out (about 100 seconds on most Cloudflare plans). Time the request directly on the server:
curl -o /dev/null -s -w '%{http_code} %{time_total}s\n' --resolve example.com:443:127.0.0.1 https://example.com/slow-pageFind out why the request is slow
- PHP slow log: in the FPM pool set
request_slowlog_timeout = 5sandslowlog = /var/log/php-fpm-slow.log. It records a stack trace of slow requests. - Database: enable the MySQL slow query log, or run
SHOW FULL PROCESSLIST;while the page loads to catch long queries or lock waits. - External calls: payment gateways, APIs or license checks without timeouts can hang a request. Set short HTTP timeouts in the code.
- Worker exhaustion: if all PHP-FPM or app workers are busy, new requests wait in a queue until Nginx gives up. Check the FPM status page for a non-zero listen queue.
- Server overload: high CPU iowait or swapping makes everything slow (
top,vmstat 1).
Align the timeouts
When a request legitimately needs longer (an admin export, a report), every layer must allow it. Keep the outer layers slightly longer than the inner ones:
| Layer | Setting | Default |
|---|---|---|
| Nginx → PHP-FPM | fastcgi_read_timeout | 60s |
| Nginx → app server | proxy_read_timeout | 60s |
| PHP-FPM pool | request_terminate_timeout | 0 (off) |
| PHP | max_execution_time | 30s |
| Gunicorn | --timeout | 30s |
Example for one admin path only, so normal pages keep tight limits:
location ^~ /admin/export {
fastcgi_read_timeout 300s;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}Reload Nginx after editing: nginx -t && systemctl reload nginx.
Move long work out of the request
Anything that takes minutes belongs in a background job (queue worker, cron, CLI command) that the page starts and then polls for status. This avoids timeouts at the CDN and the browser altogether.
Check the fix
- The slow request now completes under the timeouts, measured with curl.
- The Nginx error log has no new
upstream timed outlines.
Troubleshooting
- 504 on every page suddenly: the backend is hung or overloaded: database locked, disk full, PHP-FPM workers all stuck. Restart cautiously and read the logs.
- Raised timeouts, still 504 after 100 s: Cloudflare or a load balancer has its own limit.
- 504 only for uploads: slow client uploads; check
client_body_timeoutand upload size limits.
504 Gateway Timeout FAQ
Should I just set all timeouts to 10 minutes?
No. Long timeouts let stuck requests pile up and exhaust workers. Raise them only where needed.
Can a 504 be caused by DNS?
Rarely: if Nginx proxies to a hostname that resolves slowly or wrongly. Use IPs or a reliable resolver for upstreams.
Does this apply to cPanel hosting?
The causes are the same, but server timeouts are managed by the host. Optimise the script or ask support.
