← Blog · Engineering Operations · September 17, 2026 · 6 min read · By Operate Technical Staff

Every Hop Has a Timer. Write Them Down in Order and You Will Usually Find One Inverted.

A 504 names the hop whose timer expired, not the hop that was slow. Build the timeout ladder for your whole path, check the invariant, find the inversion.

Every Hop Has a Timer. Write Them Down in Order and You Will Usually Find One Inverted.

Every Hop Has a Timer. Write Them Down in Order and You Will Usually Find One Inverted.

TL;DR: A 504 gateway timeout occurs when a server acting as a gateway or proxy fails to receive a timely response from an upstream server. It identifies the specific hop whose timer expired, but it does not necessarily name the component that was slow; resolving it requires auditing the entire request path for timeout inversions.

The 504 names the hop that gave up, not the hop that was slow

A common misconception is that a 504 gateway timeout means the application is broken. In reality, a 504 is a statement about a gateway’s own internal configuration. It means the gateway reached its pre-defined limit for waiting on a response.

According to [MDN Web Docs], a 504 status code is returned when a server acting as a proxy cannot get a response in time to satisfy its own timeout budget. This distinguishes it from a 502 Bad Gateway, which implies an invalid response or a connection refusal. When you see a 504, you aren’t looking at a diagnosis; you are looking at the moment a specific timer in your stack hit zero.

Draw your path before you touch anything

Modern production environments are no longer simple "client-to-server" connections. A typical request path often includes a CDN, a WAF, a cloud load balancer (like an AWS ALB), an ingress controller (like Nginx), a service mesh sidecar (like Envoy), the application server (Spring Boot, FastAPI, or Node.js), and finally the database.

Each of these stages—or "hops"—has its own independent timeout configuration. Most engineers have between six and ten of these timers running simultaneously for every single request. If you haven't written them down in a single list, you likely have a latent 504 waiting to trigger.

The ladder: every timer, its default, and the doc that defines it

To understand what causes 504 gateway timeout events in your specific stack, you must audit the "ladder" of defaults. The following table represents the most common defaults across distributed paths.

Hop Directive / Setting Default Value Evidence / Source
Cloudflare Error 524 (Timeout) 125 seconds [Cloudflare Docs]
AWS CloudFront Origin Response Timeout 30 seconds [AWS CloudFront Docs]
AWS ALB Idle Timeout 60 seconds [AWS ELB Docs]
AWS ELB Target Connection Timeout 10 seconds [AWS ELB Docs]
Nginx Ingress proxy_read_timeout 60 seconds [Nginx.org]
Nginx Ingress proxy_connect_timeout 60 seconds [Nginx.org]
Apache ProxyTimeout / Timeout 60 seconds [Apache.org]
HAProxy timeout server (No default) [HAProxy.org]
PHP-FPM request_terminate_timeout 0 (Unlimited) [PHP.net]
Postgres statement_timeout 0 (Unlimited) [PostgreSQL.org]

Key Takeaway: A 504 is often caused by an unset inner timeout (like a database statement timeout) allowing a process to run longer than the outer gateway's budget.

The invariant

For your system to behave predictably, you must satisfy a single checkable invariant: For any adjacent pair of hops, the outer budget must exceed the inner budget multiplied by the number of attempts (retries) the outer hop will make, plus the connection time.

When this invariant is violated, you encounter two canonical patterns:

  1. Inversion: Your application has a 75-second timeout, but your ALB has a 60-second idle timeout. The ALB returns a 504 while the app is still processing. The client sees a failure, but the work continues.
  2. Equality: Both the proxy and the app are set to exactly 60 seconds. They enter a race condition where the emitting hop is a coin flip, making logs nearly impossible to correlate across deploys.

What happens to the work the gateway abandoned

The most dangerous part of a 504 gateway timeout the server didn't respond in time error is what happens to the "orphaned" work. Timing out is not the same as cancelling.

When an outer timer (like a CDN) fires, the inner hop (the application) usually has no idea the client has left. It continues to hold the database connection and occupy the CPU. If the client or proxy then retries the request, that retry arrives at a backend that is still occupied by the first, abandoned request. This leads to [retry-storm-amplification-factor], where an inverted ladder converts a single slow dependency into a cascading queue that never drains.

Reading a 504 you did not expect

If you are wondering how to fix a 504 gateway timeout during an active incident, follow this diagnostic order:

  1. Duration Fingerprint: Does the 504 arrive in exactly 30, 60, or 120 seconds? If the response time is a round number, it almost certainly matches a vendor default in your ladder.
  2. Header Attribution: Inspect the headers. Server, Via, X-Amzn-Trace-Id, CF-Ray, and X-Envoy-Upstream-Service-Time are signatures that tell you exactly which box generated the error.
  3. Emitting Hop Logs: Check the logs of the hop identified in step two. Do not start with application logs; the application often believes the request was successful because it finished after the gateway gave up.
  4. Upstream Latency: Only after identifying the timer should you look at the upstream (e.g., a slow database query) that caused the delay.

The audit, as a checklist you can run this week

To prevent future incidents, conduct a configuration audit:

When a 504 is not yours

Is a 504 gateway timeout my fault? If you are a visitor, the answer is usually no; it is a server-side configuration issue. For engineers, if the emitting hop is a managed provider (like an AWS API Gateway), and their status page is green, check your egress. A 504 from a provider often means your own service failed to respond to them within their hard-coded limits.

Why this matters now

The number of timers in the standard request path has doubled in recent years with the rise of service meshes and WAFs. Because this "ladder" crosses infrastructure, code, and database boundaries, it rarely has a single owner. This lack of ownership leads to "operational debt"—configurations that work fine under normal load but collapse into retry storms the moment latency spikes.

Operate addresses this by reading the configuration across the entire path, from the load balancer to the database. When it detects a timeout inversion or an unbounded budget, it identifies the risk before it causes an incident. Operate then proposes the fix as a PR for a human to review, ensuring that your timeout ladder remains monotonic and your system stays resilient.

Sources & further reading

#sre#infrastructure#networking#devops#timeouts