Skip to main content
504

Gateway Timeout

Active
RFC 9110 §15.6.5Since 1997Reverse proxies, API gateways, CDNs, load balancers, microservice meshes

HTTP 504 Gateway Timeout indicates the server, acting as a gateway or proxy, did not receive a timely response from the upstream server needed to complete the request. Defined in RFC 9110 §15.6.5. Distinct from 502 (invalid response) and 408 (client slowness).

Description

The 504 Gateway Timeout status code means the server, while acting as a gateway or proxy, did not receive a timely response from an upstream server it needed to access in order to complete the request. The gateway had a timeout configured, the upstream exceeded it, and the gateway gave up waiting. The client's request may have been perfectly valid — the problem is entirely between the proxy and the origin.

This is fundamentally different from two commonly confused codes. HTTP 502 Bad Gateway means the proxy DID receive a response from upstream, but that response was invalid or malformed — it got garbage back. HTTP 504 means it got NOTHING back within the allowed time window. And HTTP 408 Request Timeout means the CLIENT was too slow sending its request to the server — a completely different direction of slowness.

The timeout that fires is always configured on the proxy/gateway itself, not the upstream. Nginx uses proxy_read_timeout (default 60s). AWS ALB has an idle timeout (default 60s). Cloudflare enforces a 100-second timeout to origin servers. When the upstream takes longer than that configured window to begin sending a response, the proxy closes the connection and returns 504 to the client.

In microservice architectures, 504 is the canary for cascading failures. One slow service (often caused by database connection pool exhaustion, cold starts in serverless functions, or a downstream dependency of its own) causes its callers to time out. Those callers then appear slow to THEIR callers, propagating 504s across the entire service mesh. The fix is rarely 'increase the timeout' — it's identify the bottleneck, optimize or shed load, or convert the operation to asynchronous processing (return 202 Accepted with a polling endpoint).

Retry behavior matters. Unlike 503 (which often includes Retry-After), 504 gives no indication of when the upstream might recover. Clients should implement exponential backoff with jitter. Aggressive retries against a struggling upstream make cascading failures worse — the retry storm becomes the second failure mode on top of the original slowness.

Examples

Request to a slow upstream API
http
GET /api/reports/annual-summary HTTP/1.1
Host: api.example.com
Authorization: Bearer token123
Accept: application/json
504 response — proxy timeout exceeded
http
HTTP/1.1 504 Gateway Timeout
Content-Type: application/json
X-Request-Id: req_7f3a9b2c
Retry-After: 30

{"error": "gateway_timeout", "message": "The upstream server did not respond within 60 seconds.", "request_id": "req_7f3a9b2c", "upstream": "report-service", "timeout_ms": 60000}
Request through CDN to unresponsive origin
http
GET /images/hero-banner.webp HTTP/1.1
Host: cdn.example.com
Accept: image/webp,image/png,*/*
Cache-Control: no-cache
504 response — CDN origin timeout
http
HTTP/1.1 504 Gateway Timeout
Content-Type: text/html
Server: cloudflare
CF-RAY: 8a1b2c3d4e5f-IAD
X-Cache: MISS

<html><body><h1>504 Gateway Timeout</h1><p>The origin server did not respond in time. CloudFlare was unable to reach the origin.</p></body></html>
Microservice-to-microservice timeout
http
POST /api/checkout HTTP/1.1
Host: gateway.internal
Content-Type: application/json
X-Correlation-Id: corr_abc123

{"cart_id": "cart_99", "payment_method": "pm_visa_4242"}
504 response — downstream payment service timed out
http
HTTP/1.1 504 Gateway Timeout
Content-Type: application/json
X-Correlation-Id: corr_abc123

{"error": "gateway_timeout", "message": "Downstream service 'payment-service' did not respond within 30s.", "correlation_id": "corr_abc123", "failed_at": "payment-service", "suggestion": "Retry with idempotency key or check order status before retrying."}

Edge Cases

  • 504 is caused by the PROXY's configured timeout, not the client's. The client may have a 120s timeout, but if the proxy's is 60s, the client sees 504 at 60s — increasing the client timeout won't help.
  • Cloudflare returns its proprietary 524 (not 504) when the origin server responds but the TCP connection times out. A standard 504 from Cloudflare means the origin never responded at all. Don't confuse them in error-tracking dashboards.
  • Database connection pool exhaustion is a top root cause. All connections are busy serving long queries, new requests queue, the queue grows until the proxy timeout fires. The fix is pool sizing + query optimization, not timeout increases.
  • Serverless cold starts (AWS Lambda, Google Cloud Functions) can trigger 504 if the initialization phase (loading dependencies, establishing DB connections) exceeds the proxy timeout. Pre-warming or provisioned concurrency solves this.
  • A 504 does NOT guarantee the upstream didn't process the request. The upstream may have completed the work but responded one millisecond after the proxy gave up. For mutating operations, always use idempotency keys to prevent double-processing on retry.
  • In service meshes (Istio, Envoy), 504 can be generated by the sidecar proxy, not the API gateway. Check both gateway AND sidecar timeout configurations. The effective timeout is the minimum of all proxies in the chain.
  • Keep-alive mismatches cause phantom 504s. If the upstream closes idle connections before the proxy's keep-alive timeout expires, the proxy sends a request to a dead socket, waits for a response that will never come, and eventually returns 504.
  • WebSocket upgrade requests through proxies can 504 if the proxy has a short read timeout. WebSockets are long-lived — the proxy must be configured with extended or disabled timeouts for upgraded connections.

When You'll See This

  • Upstream API takes longer than the proxy's configured timeout to respond
  • Database query runs long due to missing index, table lock, or connection pool exhaustion
  • Third-party service (payment gateway, email provider, geo-lookup) is degraded or unreachable
  • Serverless function cold start exceeds the API gateway timeout during traffic spikes
  • DNS resolution failure for the upstream hostname causes the proxy to wait until timeout
  • One slow microservice in a call chain causes cascading 504s across the entire service mesh
  • Large file upload or processing job exceeds the reverse proxy's read timeout
  • Health check passes but the upstream is partially degraded — accepting connections but responding slowly

Implementation References

LanguageConstant
Gohttp.StatusGatewayTimeout
Rusthttp::StatusCode::GATEWAY_TIMEOUT
Pythonhttp.HTTPStatus.GATEWAY_TIMEOUT
Node.jshttp.STATUS_CODES[504]
.NETHttpStatusCode.GatewayTimeout
JavaHttpURLConnection.HTTP_GATEWAY_TIMEOUT
PHPResponse::HTTP_GATEWAY_TIMEOUT (Symfony)
Ruby:gateway_timeout (Rack)

History

Introduced in HTTP/1.1 (RFC 2068, January 1997) alongside other gateway-related codes (502, 503). Semantics refined in RFC 2616 (June 1999) and consolidated into RFC 9110 (June 2022, §15.6.5) as part of the HTTP Semantics modernization. Became the most visible error code in microservice architectures where multi-hop proxy chains amplify upstream latency into user-facing timeouts.

Related Status Codes

Related Headers

FAQ

What is the difference between 502 Bad Gateway and 504 Gateway Timeout?

502 means the proxy received a response from upstream but it was invalid — malformed headers, unexpected protocol, or a nonsensical reply. 504 means the proxy received NO response at all within its configured timeout. Think of it as: 502 = 'upstream said something broken', 504 = 'upstream said nothing'. The fix paths are completely different: 502 points to bugs in the upstream (crashing, returning wrong protocol), while 504 points to performance issues (slow queries, resource exhaustion, network latency).

How do I fix recurring 504 Gateway Timeout errors?

Start by identifying WHICH upstream is slow (check proxy access logs for the upstream address and response time). Then: (1) Optimize the slow endpoint — look for N+1 queries, missing indexes, unbound result sets, or expensive computations. (2) Check database connection pool sizing — exhausted pools queue requests until timeout. (3) For inherently slow operations, convert to async processing — return 202 Accepted immediately with a Location header pointing to a status endpoint, and process the work in the background. (4) Only increase proxy timeouts as a last resort and with circuit breakers — higher timeouts without load shedding turn localized slowness into system-wide cascading failures.

Should my client retry on 504, and how?

Yes, but carefully. 504 is retryable because it indicates a transient condition — the upstream was slow, not necessarily broken. However: (1) Always use exponential backoff with jitter to avoid thundering herd retries. (2) For mutating requests (POST, PUT, DELETE), include an idempotency key — the upstream may have processed your request and timed out only on the response. (3) Limit total retries (2-3 max). (4) If the 504 response includes a Retry-After header, respect it. (5) Consider checking resource state before retrying mutating operations (GET the order status before retrying the payment).