On this page
What Is a 504 Gateway Timeout (and How Do You Fix It)?
A 504 means a proxy waited for an upstream server that did not answer in time. The fix is usually to make the slow operation faster, not to raise the timeout.
Quick answer
- A 504 means a gateway or proxy, such as Nginx or a load balancer, forwarded a request and the upstream did not respond within its configured timeout.
- It is different from a 502, which means the upstream was unreachable or returned an invalid response, and from a 500, which the application itself produced.
- The common causes are a slow database query, an external API call without a timeout, a large export, or an upstream that has too few workers.
- Raising the proxy timeout alone converts a visible error into a slow page and can make the underlying load worse.
- Fix the cause, then set timeouts that fail fast enough to leave capacity for remaining requests.
What is a 504 Gateway Timeout?
It is an HTTP status code a proxy returns when it sends a request onward to another server and that server does not respond in time. The proxy is usually Nginx, a load balancer, a CDN edge, or an API gateway, and the upstream is your application or another service. The timeout is the proxy’s, not the client’s and not the application’s: the proxy gave up waiting and answered on the upstream’s behalf.
That is why a 504 tells you where to look and not what is wrong. The application may be slow, or fast but behind a dependency that is slow, or healthy but unable to handle the request because it has more work than workers. In every case, the response that failed is one the proxy had already accepted, so the proxy is reporting a deadline it set rather than an error the application raised.
How it differs from 502, 500, and 408
A 502 means the proxy could not get a valid response at all — the upstream was refused, crashed, or replied with something unusable. A 500 comes from the application itself, which means the request reached code that failed, so the application’s logs will have the story. A 504 means the upstream was reachable and the request was delivered, but the answer did not arrive in time. That distinction drives the whole diagnosis: 500 is a bug, 502 is usually a process or listener problem, 504 is a latency problem.
One consequence is where to look for evidence. For a 504 the interesting information is timing — how long the request ran, what it was waiting on — rather than an exception. Slow query logs, spans from tracing, and the proxy’s upstream timing fields are the useful sources, and application error logs are often empty because nothing threw.
Where the time actually goes
Four patterns account for most 504s. A slow database query: a missing index, a table that has grown, or a query that scans. An external call without a timeout: the request waits on a third-party API until the proxy’s deadline, and if the third party is slow, so are you. A genuinely long operation: generating a report, exporting data, transcoding a file — work that takes minutes and is being done inside a request. Too few workers: the application can handle each request quickly, but they queue behind each other, so the last in line exceeds the deadline.
The distinction matters because the fixes are different. The first is an index or a query rewrite. The second is a timeout and a fallback. The third is moving the work out of the request into a background job and returning a handle. The fourth is more capacity or faster per-request work. Raising the proxy timeout fixes none of them and hides all of them.
# Nginx: the proxy timeout values, with comments on what each controls.
# Raising these hides a slow upstream; use them deliberately.
location / {
proxy_pass http://app;
# How long to wait for the upstream to respond (headers).
proxy_read_timeout 30s;
# How long to wait to establish the connection to the upstream.
proxy_connect_timeout 5s;
# How long to wait to send the request body.
proxy_send_timeout 30s;
# Log the upstream timing so slow requests are visible.
add_header X-Upstream-Time $upstream_response_time always;
}How to fix it without just raising the timeout
Start by identifying the slow request. Proxy access logs with upstream response time tell you which endpoints exceeded the deadline and how far. Application traces then decompose that time: database, external call, or computation. One number eliminates most of the guesses. If the same endpoint appears repeatedly while the rest are fast, it is that endpoint’s problem; if everything is slow at the same moments, it is capacity.
Then remove the waiting rather than extending it. Add the missing index or rewrite the query. Put an explicit timeout on every external call, with a fallback that degrades gracefully, so a slow dependency cannot consume your whole deadline. Move genuinely long operations — exports, reports, media processing — to a background job and return a job identifier immediately, which is the architecture that makes the operation survivable. Add capacity if the pattern is queueing.
Set the timeouts afterwards, so they reflect reality. A request that legitimately takes twenty seconds needs the proxy to allow it, but a request that should take two hundred milliseconds should be cut off early enough to free the connection for the next one. Fail-fast timeouts protect capacity; generous ones consume it. Choosing the value per route, based on measured latency, is the difference between a timeout that helps and one that hides.
Where this bites vibecoders
A generated backend has no timeouts by default. Its HTTP client will wait indefinitely, its database driver will wait indefinitely, and the only deadline in the system is the proxy’s, so one slow dependency turns into a 504 for every affected request. Adding explicit timeouts to outgoing calls, and moving long work into a background job, is the difference between a dependency that degrades your service and one that takes it down.
Where AI coding assistants get this wrong
- Raising the proxy timeout instead of fixing the slow operation.
- Calling external APIs with no client-side timeout, leaving the proxy’s deadline as the only limit.
- Running long exports or report generation inside a synchronous request.
- Sizing a worker pool too small for the concurrency, causing requests to queue.
- Confusing 504 with 502 and looking for a crashed process rather than a slow one.
- Ignoring upstream response time in proxy logs when the application logs are empty.
Checklist
- Confirm the status is 504 rather than 502 or 500.
- Find which endpoints exceed the deadline using upstream response time.
- Decompose that request’s time: database, external calls, computation.
- Add explicit timeouts to every outgoing request with a fallback.
- Move work that takes longer than a request should into a background job.
- Set proxy timeouts per route from measured latency, not one global value.
FAQ
What is the difference between a 502 and a 504?
A 502 means the proxy could not obtain a valid response from the upstream at all, usually because the process is down, refusing connections, or replying incorrectly. A 504 means the upstream received the request but did not answer in time, so it is a latency problem rather than an availability one. The first sends you to process health, the second to request timing.
Should I increase the Nginx proxy timeout?
Only after you know why the request is slow and have decided it is legitimately long. Raising it without that converts a clear error into a slow page, ties up the connection longer, and can worsen the underlying contention. If a request genuinely needs a long time, moving it to a background job is usually the better fix.
Why do only some requests time out?
Because the trigger is usually data-dependent or load-dependent. A query can be fast for most inputs and slow for one, an external call can be slow intermittently, and a worker pool can be adequate at low traffic and over-subscribed at high. Compare the timing of failing requests against succeeding ones with the same endpoint.
Can a CDN or load balancer cause a 504?
Yes — any proxy in the path applies its own timeout, and the shortest one wins. If your application needs more time than the outermost proxy allows, you will see its error rather than yours. Check the timeout configuration at every hop, and make sure the outermost deadline is at least as generous as what the request legitimately requires.
Related topics
- What Is a 502 Bad Gateway (and How Do You Fix It)?
- Cloudflare 525 and 526: How to Fix an SSL Handshake Failure
- Why Does My Database Connection Time Out?
- Why Is My Website Suddenly Slow?
- What Is a Reverse Proxy?
- How to Debug a Crash-Looping Container