< HTTP/1.1 504 Gateway Timeout504 Gateway Timeout: nginx, ALB and Cloudflare Fixes
Fix 504 Gateway Timeout: nginx proxy_read_timeout (60s default), ALB 60s idle, API Gateway 29s, Cloudflare 524 at 125s, with error-log strings and curl timing.
- Cacheable
- Only with explicit freshness
- Retry?
- Idempotent requests only; the origin may still be working
- Usually sent by
- Reverse proxy, load balancer or CDN
- Spec
- RFC 9110 §15.6.5
On this page
TL;DR: HTTP 504 Gateway Timeout means a proxy waited too long for an upstream server. Check the layer that answered, find the slow dependency, and then either speed the request up or raise the timeout in every layer in front of it, because the shortest timeout wins. nginx read and ALB idle timeouts default to 60 seconds; API Gateway limits depend on the API type.
What it means
RFC 9110 section 15.6.5: a server acting as a gateway or proxy did not receive a timely response from an upstream server it needed to access. The timeout can happen while connecting or waiting for response data. ALB documents a 504 when connection establishment exceeds its 10-second timeout. A refused or reset connection commonly produces 502.
Server responses and client messages below are illustrative.
HTTP/1.1 504 Gateway Time-out
Server: nginx
Content-Type: text/html
nginx writes “504 Gateway Time-out” with a hyphen, and logs:
[error] 29#29: *731 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.9, server: example.com, request: "GET /report HTTP/1.1", upstream: "http://127.0.0.1:8000/report"
What you see in your client
These messages assume the response reason is Gateway Timeout; a server can use different wording.
- Axios:
AxiosError: Request failed with status code 504. With the defaultvalidateStatus, Axios rejects this response. Inspecterror.response.status,error.response.headersanderror.response.databefore changing the request. Axios source. - fetch: the promise resolves with
response.status === 504andresponse.ok === false. Checkokyourself; acatchblock alone misses HTTP error responses. Fetch behaviour. - Python requests:
requests.exceptions.HTTPError: 504 Server Error: Gateway Timeout for url: https://example.com/api. This appears when you callresponse.raise_for_status(). Save the response body and headers before raising. Requests source. - curl -f:
curl: (22) The requested URL returned error: 504. Usecurl -sS -D - https://example.com/apiwhile diagnosing so you retain the error body. curl source. - .NET:
System.Net.Http.HttpRequestException: Response status code does not indicate success: 504 (Gateway Timeout).This is the English message fromEnsureSuccessStatusCode()with that reason phrase. InspectStatusCodeand read the body before calling it. Runtime source, message resource. - Spring WebClient:
org.springframework.web.reactive.function.client.WebClientResponseException$GatewayTimeout: 504 Gateway Timeout from GET https://example.com/api.retrieve()uses this subclass by default; readgetResponseBodyAsString(). RestTemplate usesHttpServerErrorException.GatewayTimeoutfor this 5xx status. WebClient source, RestTemplate handler.
Who sent it, and what is its timeout?
| Signal | Layer | Default timeout |
|---|---|---|
Server: nginx, log above | nginx | proxy_connect_timeout 60s, proxy_send_timeout 60s, proxy_read_timeout 60s |
Server: awselb/2.0 | ALB | Idle timeout 60s |
{"message":"Endpoint request timed out"} | API Gateway | REST APIs: up to 29s before an approved increase; HTTP APIs: 30s maximum |
X-Cache: Error from cloudfront, “CloudFront attempted to establish a connection with the origin” | CloudFront | Origin response timeout 30s |
Server: cloudflare, page “Error 524: A timeout occurred” | Cloudflare | 125s default wait for the origin response |
Server: cloudflare, “Error 504 Gateway time-out” | Origin proxy or Cloudflare timed out | Check the response body and origin logs |
The nginx proxy_read_timeout is the time between two successive reads from the upstream, not the whole request. A stream must keep the interval between upstream reads below that timeout. ingress-nginx has its own defaults: connect 5 seconds, send/read 60 seconds. nginx timeouts, ingress-nginx settings.
Measure where the time goes:
curl -s -o /dev/null -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total} code=%{http_code}\n' https://example.com/report
curl -s -o /dev/null -w 'ttfb=%{time_starttransfer} code=%{http_code}\n' http://127.0.0.1:8000/report # bypass the proxy
If the direct request is slow too, fix the app. If only the proxied request fails, fix the proxy path.
Common causes by stack
- nginx/PHP-FPM:
upstream timed out ... while reading response header from upstreamidentifies the read phase. Useproxy_read_timeoutfor HTTP backends,fastcgi_read_timeoutfor PHP-FPM anduwsgi_read_timeoutfor uWSGI; fix the slow operation shown in application logs first. FastCGI timeout, uWSGI timeout. - Express, Django or Rails behind a proxy: the gateway’s 504 can arrive while the handler still runs. Correlate the proxy request with app/database timings; apply explicit deadlines to outbound calls and move exports to background jobs. Read the emitting proxy’s log rather than looking for a framework-specific 504 exception.
- AWS ALB: a connection timeout, a response idle timeout or a TLS-handshake timeout can produce 504. Check target security groups and network ACL return traffic as well as app latency. ALB 504 causes.
- API Gateway: check the integration’s
timeoutInMillisand API type, then compare the execution log with Lambda duration. Increasing Lambda’s own timeout alone leaves the gateway limit unchanged. REST integration quotas, HTTP API quotas. - Kubernetes ingress-nginx: set the read/send annotations on the Ingress that routes this endpoint, then inspect controller logs for the upstream address and timeout phase. Ingress annotations.
Fix it
- Find the slow part: slow SQL, an external API call without a timeout, a lock, an exhausted connection pool, or a worker pool with no free workers (all gunicorn or PHP-FPM workers busy). Add timeouts to every outbound call.
- For work that routinely exceeds the proxy limit, use a job endpoint. Return
202 Acceptedwith a status URL and process in the background. - Raise timeouts at every hop, from the outermost in. The smallest value wins.
location /report {
proxy_pass http://app;
proxy_connect_timeout 5s; # failing fast on a dead host is good
proxy_read_timeout 300s; # raised for this endpoint only
proxy_send_timeout 300s;
}
# PHP-FPM
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 300s;
}
# ingress-nginx
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
Check worker deadlines too: Gunicorn’s --timeout defaults to 30 seconds of worker silence; for non-sync workers it is not a per-request deadline. A killed worker can leave nginx reporting a prematurely closed connection and 502. Inspect WORKER TIMEOUT, PHP max_execution_time/FPM request_terminate_timeout, or uWSGI harakiri. Gunicorn timeout, FPM configuration.
AWS, Cloudflare and others
- ALB: raise the load balancer attribute
idle_timeout.timeout_seconds. Keep backend keep-alive timeouts longer than it (see 502). - API Gateway: Regional/private REST APIs can request an integration limit above 29 seconds, potentially reducing the account throttle quota. Edge-optimized REST APIs remain capped at 29 seconds. HTTP APIs have a separate, non-increasable 30-second maximum. Apply the approved value to the integration and redeploy.
- CloudFront: raise the origin response timeout in the origin settings (30s default; values above the default quota need a quota increase).
- Cloudflare: its default 125-second origin read timeout produces 524; Enterprise can raise it to 6,000 seconds. A Cloudflare 504 needs its own origin-versus-edge diagnosis. Use polling for jobs that exceed the available wait.
Client-side handling
Retry a 504 only if the request is safe to repeat. A GET is fine with backoff; a POST may have been processed even though the response never arrived, so use an idempotency key or check state before retrying.
Related codes
- 502 Bad Gateway: connection refused, reset or invalid response from the upstream.
- 503 Service Unavailable: no healthy upstream, or deliberate unavailability.
- 524 A Timeout Occurred: Cloudflare connected, then hit an origin read or write timeout.
- 408 Request Timeout: the client was too slow, the reverse of this.
- 499 Client Closed Request: nginx logs this when the client gave up before the upstream answered.
See also the comparison 502 vs 503 vs 504: how to tell the three gateway errors apart from the nginx error log and Cloudflare codes.
Frequently asked questions
What does 504 Gateway Timeout mean?
A proxy, load balancer or CDN did not receive a response from the upstream server in time. The timeout can occur during connection establishment or while waiting for response data; slow dependencies and blocked network paths both need investigation.
How do I fix nginx 504 Gateway Time-out?
Find out why the upstream is slow first. If the request legitimately takes longer, raise proxy_read_timeout (default 60s) for that location, or fastcgi_read_timeout for PHP-FPM and uwsgi_read_timeout for uWSGI, and check that other layers in front of nginx do not time out sooner.
What is the default timeout for 504 on AWS ALB, API Gateway and CloudFront?
ALB idle timeout is 60 seconds by default and configurable up to 4000 seconds. REST API integrations support up to 29 seconds before an approved increase; Regional and private APIs can request more, potentially reducing the account throttle quota, while edge-optimized APIs cannot. HTTP APIs have a separate 30-second maximum. CloudFront origin response timeout defaults to 30 seconds; change the origin setting after approval if a quota increase is needed. Whichever limit is shortest wins.
What is the difference between Cloudflare 504 and 524?
Cloudflare 524 means the TCP connection to your origin succeeded, then an origin read or write timed out. The read limit defaults to 125 seconds and Enterprise can raise it; the separate write limit is 30 seconds. A 504 is a gateway timeout returned by Cloudflare or an origin proxy; compare the error body and origin logs to locate it.
What is the difference between 504 and 408?
504 is the server-side proxy giving up on its upstream. 408 Request Timeout is a server saying the client was too slow sending its request.
Sources
Related
502 vs 503 vs 504: Which Gateway Error Is It
502, 503 and 504 side by side: who generated each one, what the upstream did, the nginx error-log line that proves it, and the Cloudflare 52x and ALB equivalents.
501 Not Implemented
The server doesn't support the functionality required to fulfill the request. Learn about unimplemented features.
505 HTTP Version Not Supported
Learn what 505 HTTP Version Not Supported means when servers reject protocol versions. Understand HTTP/1.1, HTTP/2 compatibility and version negotiation.
506 Variant Also Negotiates
506 Variant Also Negotiates is a server configuration error in transparent content negotiation (RFC 2295). See what triggers it in Apache and how to fix it.
507 Insufficient Storage: What the Error Means
507 means the server ran out of space to store what your request needs. Where it comes from, including WebDAV, uploads and quotas, and what to check.