HTTP status code · Server errors (5xx)
504 Gateway Timeout
504 Gateway Timeout means a proxy or gateway did not get a response from the upstream server in time and gave up. The most common cause is a slow request in the application, such as a heavy database query or a call to an external API, that takes longer than the proxy's timeout (60 seconds by default in nginx).
| Class | 5xx, Server errors |
|---|---|
| Defined in | RFC 9110 §15.6.5 |
| Cacheable by default | Only with explicit Cache-Control or Expires |
| Safe to retry | Yes for idempotent requests, with backoff; for POSTs, check first whether the work already happened |
| Relevant headers |
|
What 504 means
RFC 9110, section 15.6.5, defines 504 as a gateway or proxy not receiving a timely response from an upstream server it needed to complete the request. Every hop has its own clock: the CDN, the load balancer, nginx and the application server can each time out, and the first one to run out of patience is the one that sends the 504.
A 504 does not mean the work stopped. The application may still be running the query or finishing the payment after the proxy has already told the client it failed. That is why blindly retrying a POST after a 504 can create duplicates, and why long operations should return 202 Accepted and run in the background.
The default limits are worth knowing. nginx waits 60 seconds between two reads from the upstream (proxy_read_timeout) and at most 60 seconds to connect (proxy_connect_timeout). An AWS Application Load Balancer returns 504 when it cannot connect to a target within 10 seconds or the target stays silent past the idle timeout.
Common causes
If you are visiting the site
- The page or action you requested triggers slow work on the server, such as a large report, a search or an export.
- The site's backend or one of its providers is overloaded and responding slowly for everyone.
- The site is behind a CDN that cannot get a timely response from the origin server.
If you run the server
- A slow database query, often a missing index or a lock held by another transaction; nginx logs "upstream timed out (110: Connection timed out) while reading response header from upstream".
- An outbound call to a third-party API with no timeout of its own, so the request waits as long as the provider does.
- A long job done inside the request (report generation, file processing, sending hundreds of emails) instead of in a background worker.
- A proxy timeout shorter than the work you legitimately expect, or mismatched timeouts across layers, for example Cloudflare waiting 125 seconds in front of an nginx that gives up at 60.
- Network problems between proxy and app: a firewall silently dropping packets, a wrong security group or a DNS name that resolves to an unreachable address.
How to fix it
If you are visiting the site
- Reload after a minute; if the server was only briefly overloaded, the next attempt often works.
- For a heavy action such as an export, try a smaller date range or fewer items.
- Before resubmitting a payment or order, check your email or account: the first attempt may have gone through.
If you run the server
- Time the request against the app directly, bypassing the proxy, to see how long it really takes; then profile the slow part (query plans with EXPLAIN, traces, slow query log).
- Set explicit timeouts on every outbound call, shorter than your proxy timeout, and return a clear error when they fire.
- Move long work to a queue: answer 202 Accepted with a status URL and let the client poll or receive a webhook.
- Raise proxy_read_timeout only for the routes that legitimately need it, and align timeouts so the outer layers wait a little longer than the inner ones.
- If even the connection fails, check firewalls, security groups and that the upstream address in the proxy config is reachable from the proxy host.
How to send 504
Like 502, a 504 is produced by the proxy, not your handler. The Go example is for when you write the gateway yourself; the nginx block raises the timeout for one slow route only.
target, _ := url.Parse("http://127.0.0.1:9000")
proxy := httputil.NewSingleHostReverseProxy(target)
t := http.DefaultTransport.(*http.Transport).Clone()
t.ResponseHeaderTimeout = 30 * time.Second
proxy.Transport = t
// ReverseProxy answers 502 for every upstream error; report timeouts as 504
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
var ne net.Error
if errors.As(err, &ne) && ne.Timeout() {
w.WriteHeader(http.StatusGatewayTimeout) // 504
return
}
w.WriteHeader(http.StatusBadGateway) // 502
}location /reports/ {
proxy_pass http://app;
proxy_connect_timeout 5s;
# Default 60s; a gap longer than this between reads becomes a 504
proxy_read_timeout 180s;
proxy_send_timeout 60s;
}# How long does the app take without the proxy?
curl -o /dev/null -s -w '%{http_code} %{time_total}s\n' http://127.0.0.1:3000/reports/annual
# Timeouts nginx recorded
grep 'upstream timed out' /var/log/nginx/error.log | tail -n 20Commonly confused with
- 504 vs 502
- 504 means the upstream stayed silent too long; 502 means it answered with something invalid or dropped the connection.
- 504 vs 408
- 408 Request Timeout is the server tired of waiting for the client to finish sending; 504 is a proxy tired of waiting for another server to reply.
- 504 vs 524
- Cloudflare reports its own origin timeout as 524, after connecting successfully and waiting 125 seconds by default, rather than as 504.
- 504 vs 499
- In nginx logs, 499 means the client gave up before nginx did; 504 means nginx gave up on the upstream first.
Frequently asked questions
- How do I fix a 504 Gateway Timeout in nginx?
- First find out why the upstream is slow by timing it directly. If the slowness is legitimate, raise proxy_read_timeout (and fastcgi_read_timeout for PHP-FPM) for that location only. If it is not, fix the slow query or external call, or move the work to a background job.
- Is a 504 error my internet connection?
- Almost never. Your request reached the site's proxy, which is why you got a response at all. The timeout happened between servers on the site's side.
- Is it safe to retry after a 504?
- For GET and other idempotent requests, yes, with a delay. For a POST such as a payment, the upstream may have completed the work after the proxy gave up, so check the result first or use an idempotency key.
- What is the difference between 504 and 408?
- 408 Request Timeout means the server waited too long for the client to send its request. 504 Gateway Timeout means a proxy waited too long for an upstream server to send the response.
- Why do I get a 504 after exactly 60 seconds?
- Sixty seconds is the default for nginx proxy_read_timeout and fastcgi_read_timeout and for the AWS load balancer idle timeout. A 504 at a round number almost always points to one of those defaults being hit.
Last reviewed by Arielton Oberek.