504 Gateway Timeout
504 Gateway Timeout means a proxy or load balancer forwarded your request to an upstream server and gave up waiting for its response. The proxy is fine; the upstream is slow or stuck.
A proxy or gateway waited too long for the server behind it to respond.
The upstream accepted the connection or request but did not reply within the proxy's timeout, for example nginx's proxy_read_timeout (60 seconds by default). The upstream may still be working on the request and finish it after the client has given up, which matters for non-idempotent operations. Find the slow query or external call before raising timeouts.
Explained
The proxy-to-upstream hop is the one that failed, and it failed by timing out rather than by being refused (that would be 502). nginx logs upstream timed out (110: Connection timed out) while reading response header from upstream when proxy_read_timeout (60 seconds by default) runs out, and while connecting to upstream when proxy_connect_timeout does.
Other layers have their own limits: an AWS Application Load Balancer's idle timeout is 60 seconds by default, and Cloudflare reports its 100-second timeout as 524 rather than 504. The shortest timeout in the chain wins, so check every hop.
A 504 does not mean the work stopped. The upstream may finish the request after the client gave up, so a retried POST can run twice. For long jobs, return 202 Accepted with a status URL and do the work in the background instead of raising timeouts.
How to debug it
- Read the proxy error log to see which timeout fired: connect, send or read.
- Find the same request in the upstream's logs and measure how long it actually took.
- Profile the slow path: slow database queries, lock waits, calls to external APIs without their own timeout, exhausted connection pools.
- List every timeout between the client and the app (CDN, load balancer, nginx, app server) and make them consistent, with outer layers slightly longer than inner ones.
- Raise a timeout only for endpoints that are legitimately slow, for example in a dedicated nginx
locationblock.
Gotchas
- If the app server's own timeout is shorter than the proxy's, the worker gets killed first and you see a 502 instead of a 504.
- Streaming responses reset nginx's read timer with every chunk, so a long download is fine as long as data keeps flowing.
- A 504 that hits every request at once is usually a stuck shared dependency (database, DNS, a full connection pool), not a slow endpoint.
Related explainers
- 502 Bad GatewayA 502 means a proxy got no valid reply from the app behind it. How to read nginx's error log, common...
- 503 Service Unavailable503 means the server is temporarily unable to handle requests. Common sources: no healthy backends, nginx...
- 499 Client Closed Request (nginx)499 is an nginx log code: the client hung up before the response was sent. What causes it, why the client...
- 500 Internal Server ErrorA 500 is the server's generic failure code, usually an unhandled exception. Where to find the real error...