HTTP 504 Gateway Timeout: Tracing the Slow Upstream
People blame the wrong server for a 504 almost every time. The error page says "Gateway Timeout," the gateway is nginx or Cloudflare, so the instinct is that the gateway failed. It didn't. The 504 status code means the gateway was working perfectly — it forwarded your request upstream, started a timer, and the upstream failed to respond before the timer expired. Nothing crashed. Nothing refused a connection. Something was just slow, and something else in front of it had a deadline.
That framing gives you the whole diagnostic strategy: a 504 is always a race between a duration and a timeout, so you need to find out which layer burned the time, and then decide whether the duration or the deadline is the thing to fix.
504 status code: the gateway didn't fail, it gave up
Compare it to its sibling: a 502 Bad Gateway means the upstream answered wrongly — refused connection, reset, garbage bytes. A 504 means the upstream didn't answer at all within the allowed window. Dead-versus-slow is the fork in the road: 502 sends you looking for crashed processes and wrong ports, 504 sends you looking for slow queries and stuck requests. If your errors flap between 502 and 504 during the same incident, you likely have workers that are both dying and slow — usually memory pressure, where some requests OOM (502) and others grind in swap (504).
Where the seconds go: the timeout chain
A production request passes through a series of components, each with its own patience. Whichever timeout is shortest fires first and stamps the error:
| Layer | Typical timeout | What it's waiting on |
|---|---|---|
| Browser | ~300s | The whole response |
| CDN edge (e.g. Cloudflare) | ~100s (fixed on most plans) | First byte from origin |
| Load balancer | 60s idle (AWS ALB default) | Activity from targets |
nginx (proxy_read_timeout) | 60s | Reads from the app upstream |
| App server (e.g. gunicorn worker timeout) | 30s | The worker to finish |
| Database client | Often unlimited | The query — and this is the trap |
The trap in that last row: many database clients default to no timeout at all. So a query that takes four minutes doesn't fail — it holds a worker hostage while every timeout above it expires in sequence. The user sees whichever layer gave up first; the root cause sits at the bottom of the chain, patiently still running.
Typical culprits behind HTTP 504
- Slow database queries — the number one cause by a wide margin. A missing index that didn't matter at 10,000 rows matters enormously at 10 million; a report query on an unindexed date column; a lock held by a migration while regular queries queue behind it.
- Slow third-party calls in the request path — a payment provider or external API having a bad day, called synchronously with a generous or absent timeout, transfers their latency directly onto your users.
- Worker starvation — a few genuinely slow requests occupy all workers, and fast requests queue behind them until they, too, blow the deadline. One slow endpoint can 504 the whole site this way.
- Undersized timeouts — occasionally the work is legitimately long (a big export, a complex search) and a 30-second worker timeout is simply wrong for that route. This is the one case where raising the timeout is the actual fix.
- Cold starts and thundering herds — an app that takes 90 seconds to warm up after deploy, or a cache expiry that sends a thousand simultaneous requests at one expensive query.
Pinning down the slow layer
Measure the same request at different depths of the stack. From outside, curl gives you the full picture in one line: curl -o /dev/null -sw 'ttfb: %{time_starttransfer}s total: %{time_total}s\n' https://yoursite.com/slow-page/. Run the same measurement against the origin directly (bypassing the CDN) and against the app port directly (bypassing nginx). Wherever the number collapses from "times out" to "fast," you've bracketed the slow layer.
Then read the timing your infrastructure already records: nginx's $upstream_response_time in the access log tells you exactly how long the app took for every request — sort those descending and the slow endpoints name themselves. At the database, the slow query log (long_query_time = 1 in MySQL, log_min_duration_statement in Postgres) catches the queries burning the time; EXPLAIN on the worst offender usually reveals the missing index in about thirty seconds. Slowness also tends to announce itself gradually — response times creep up for weeks before the first 504 — which is why tracking server response time over time catches these problems while they're still cheap.
SEO fallout from repeated 504s
Googlebot treats 504 like the other 5xx codes: tolerated briefly, punished when chronic. Crawl rate drops when the crawler keeps hitting timeouts, fresh content gets picked up slower, and URLs that 504 persistently for weeks fall out of the index. 504s have an extra SEO wrinkle the other errors don't: they correlate with load, so they disproportionately hit your busiest pages at your busiest hours — which is exactly where crawl demand concentrates too. A site that 504s under load is invisibly rationing its own crawlability. The other wrinkle is timing: because slow pages that don't quite time out still get crawled, the first 504 Google sees often means the problem's been building for a while. Audit your site for slow pages before the slowness matures into timeouts — a crawl report showing response times per URL is the early-warning version of this error.
Prevention: budgets, indexes, and sane deadlines
Give every layer a latency budget that fits inside the layer above it: if nginx waits 60 seconds, the app should cap its own work around 30, and the database client should time out near 10. That inversion — tightest at the bottom — means failures happen where the context is, producing a fast, loggable database error instead of a mute upstream timeout. Set a statement timeout at the database (statement_timeout in Postgres does this globally). Move anything legitimately slow — exports, imports, report generation — out of the request path and into background jobs that report progress. Keep an eye on p95/p99 latency, not averages, because timeouts live in the tail. And resist the reflexive fix of raising every timeout after an incident: longer deadlines don't make anything faster, they just make users wait longer to see the same error while workers stay hostage longer. Fix the duration, not the deadline — unless the deadline was genuinely wrong for the work.
Frequently Asked Questions
What's the difference between a 502 and a 504 error?
A 502 means the upstream server answered but the answer was unusable — a refused connection, a reset, or invalid bytes. A 504 means it never answered within the gateway's time limit. In practice: 502 points at a dead or misconfigured process, 504 points at something slow, usually a database query.
Should I increase my proxy timeout to fix 504 errors?
Only if the work behind the timeout is legitimately long, like a big export on a specific route. For normal pages, raising the timeout just makes users wait longer for the same failure while tying up server workers. The durable fix is finding what's slow — nearly always a query — and speeding it up or moving it to a background job.
Why does my site only throw 504 errors during peak traffic?
Load amplifies latency. Queries that take 2 seconds when the database is idle take 40 under contention, and a few slow requests can occupy all your workers so that even fast requests queue past the deadline. The fix is capacity plus query optimization, not timeout adjustments — the peak-hour pattern is telling you where the ceiling is.
Can Cloudflare cause 504 errors on its own?
Cloudflare generates the 504 page, but the cause is your origin taking longer than Cloudflare's roughly 100-second wait for a first byte, or your own proxy timing out behind it. Cloudflare's timeout is fixed on most plans, so the resolution is always on the origin side: find and fix whatever is consuming the time.
Try WebsiteChecker.Tech Free
Run a free technical SEO audit on any website. Get a client-ready report in minutes.
Start Free Scan