How HTTP Works
5xx · Server error
< HTTP/1.1 504 Gateway Timeout

504 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.

Reviewed 6 min readintermediate5 sourcesTry itMarkdown
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
Often confused with
502, 408
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 default validateStatus, Axios rejects this response. Inspect error.response.status, error.response.headers and error.response.data before changing the request. Axios source.
  • fetch: the promise resolves with response.status === 504 and response.ok === false. Check ok yourself; a catch block 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 call response.raise_for_status(). Save the response body and headers before raising. Requests source.
  • curl -f: curl: (22) The requested URL returned error: 504. Use curl -sS -D - https://example.com/api while 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 from EnsureSuccessStatusCode() with that reason phrase. Inspect StatusCode and 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; read getResponseBodyAsString(). RestTemplate uses HttpServerErrorException.GatewayTimeout for this 5xx status. WebClient source, RestTemplate handler.

Who sent it, and what is its timeout?

SignalLayerDefault timeout
Server: nginx, log abovenginxproxy_connect_timeout 60s, proxy_send_timeout 60s, proxy_read_timeout 60s
Server: awselb/2.0ALBIdle timeout 60s
{"message":"Endpoint request timed out"}API GatewayREST 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”CloudFrontOrigin response timeout 30s
Server: cloudflare, page “Error 524: A timeout occurred”Cloudflare125s default wait for the origin response
Server: cloudflare, “Error 504 Gateway time-out”Origin proxy or Cloudflare timed outCheck 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 upstream identifies the read phase. Use proxy_read_timeout for HTTP backends, fastcgi_read_timeout for PHP-FPM and uwsgi_read_timeout for 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 timeoutInMillis and 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

  1. 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.
  2. For work that routinely exceeds the proxy limit, use a job endpoint. Return 202 Accepted with a status URL and process in the background.
  3. 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.

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

  1. MDN Web Docs: 504 Gateway Timeoutdeveloper.mozilla.org
  2. RFC 9110 Section 15.6.5: 504 Gateway Timeoutrfc-editor.org
  3. nginx: proxy_read_timeoutnginx.org
  4. AWS: Troubleshoot Application Load Balancer HTTP 504docs.aws.amazon.com
  5. Cloudflare: Error 502 and 504developers.cloudflare.com

Keep going

Browse /search