504 Gateway Timeout: what it means and how to fix it

  • http-errors

A 504 and a 502 are siblings with different tempers. A 502 means the upstream answered wrongly; a 504 means it didn’t answer in time. The proxy waited — typically 30 or 60 seconds — gave up, and told you so. Something behind it is alive but too slow.

What’s actually being slow?

The request chain is usually browser → CDN → proxy → app → database, and a 504 means one link exceeded the link in front of it. The classic culprits, most common first:

  • A database query gone quadratic — a missing index, a lock queue, a table that grew past its plan.
  • A downstream API call with no timeout of its own — your app waits forever on a third party, so your proxy times out on your app.
  • CPU or memory saturation — the app is thrashing and everything takes 40× normal.
  • Cold starts — serverless or just-restarted apps paying startup cost on the first requests.

How do I find the slow hop?

Time each layer independently:

# Total, through the front door — where does the time go?
curl -s -o /dev/null -w 'dns %{time_namelookup}  connect %{time_connect}  ttfb %{time_starttransfer}  total %{time_total}\n' https://example.com

# The app directly, from its own box
curl -s -o /dev/null -w 'ttfb %{time_starttransfer}\n' http://127.0.0.1:3000/

# The database, from the app's box
time psql -c 'SELECT 1'

A high time-to-first-byte at the app layer with a fast database points at application code. Fast app-direct but slow through the proxy points at the proxy’s own config or a saturated connection pool between them.

Should I just raise the timeout?

Usually no. The proxy’s timeout is a circuit breaker, not a nuisance — raising it converts fast failures into slow failures and ties up worker connections while requests queue behind the slow ones. Raise it only when the endpoint is legitimately slow (a report generator, an export) and then only for that route. Fix the slowness for everything else.

Why do 504s deserve monitoring more than most errors?

Because they creep. The query that takes 4 seconds today takes 31 seconds after three months of table growth, and the first 504 lands at your peak traffic hour. Response-time history shows the creep long before the timeout: YoPingMe keeps 30 days of it on the free plan, checks from Frankfurt, Virginia, and Singapore, and only alerts when all three regions agree. Ten monitors free — the slow creep stops being a surprise.