504 Gateway Timeout: What It Means and How to Fix It
A 504 means the proxy in front of your app (Nginx, Cloudflare, a load balancer) gave up waiting for a response. The usual causes — slow database queries, long tasks done in the request, external APIs, timeouts set too low — and how to find and fix each.
504 Gateway Timeout means: the server in front of your app — a reverse proxy like Nginx or Caddy, Cloudflare, or a load balancer — forwarded the request to your app and gave up waiting for the answer.
Your app might still be working on it. It just took longer than the proxy was willing to wait. (Reverse proxies explained)
504 vs 502
| Code | The proxy is saying |
|---|---|
| 502 Bad Gateway | "The app gave me no valid answer" — crashed, refused, wrong port |
| 504 Gateway Timeout | "The app took too long" |
A 502 points at a dead or unreachable app. A 504 points at a slow one.
Typical timeouts
- Nginx:
proxy_read_timeoutdefaults to 60 seconds. - Cloudflare: about 100 seconds on most plans, then it shows its own 524 timeout page.
- Serverless platforms: function limits, often 10–60 seconds depending on plan.
- Load balancers: commonly 60 seconds.
If a request regularly takes close to a minute, something is wrong — users won't wait that long either.
Cause 1: a slow database query
The most common cause. A query that was fast with 100 rows takes 40 seconds with 2 million because it's scanning the whole table.
- Log slow queries and check them with
EXPLAIN ANALYZE. (Reading EXPLAIN ANALYZE) - Add the missing index. (Database indexes)
- Watch for loops that run one query per item. (N+1 queries)
- Check for locks — a query waiting on another transaction can hang. (Postgres deadlocks)
Cause 2: long work done inside the request
Generating a big report, processing a video, sending 5,000 emails, calling an AI model several times — these don't belong in a web request.
Move them to a background job: the request starts the job and returns immediately; the user is notified (or the page polls) when it's done. (Background jobs)
Cause 3: a slow external API
Your app calls a payment provider, an AI model or another service, and that service is slow or down. Your request waits with it.
- Set timeouts on outgoing calls so they fail fast instead of hanging.
- Retry sensibly. (Exponential backoff)
- For AI responses, stream them so data starts flowing immediately. (Streaming LLM responses)
Cause 4: the server is overloaded
Too many requests at once, not enough CPU or memory, or the database's connections are all in use, so new requests queue. Check CPU and memory graphs during the slowdown. (Load testing, Postgres connection pooling)
Cause 5: the app hangs
A bug — an unresolved promise, a deadlock, an infinite loop — means the request never finishes. The app's logs will show the request starting but not ending.
How to find which it is
Reproduce and time it from the server itself, bypassing the proxy:
time curl -s -o /dev/null http://127.0.0.1:3000/slow-pageCheck the app's logs around that time for slow queries or errors.
Check the database for long-running queries:
SELECT pid, now() - query_start AS duration, query FROM pg_stat_activity WHERE state = 'active' ORDER BY duration DESC;Check server resources —
toporhtopfor CPU and memory.
Should you just raise the timeout?
Sometimes, briefly — for a known slow admin export, say:
location /admin/export {
proxy_pass http://127.0.0.1:3000;
proxy_read_timeout 300s;
}
But raising timeouts usually hides the problem. Fix the slow part, or move the work to the background.
The summary
- 504 = the proxy waited too long for your app.
- Usually a slow query, long work in the request, or a slow external service.
- Measure from the server, read the logs, check the database.
- Fix the slowness; use background jobs for long tasks.
EasySpawn runs your app, its database and a background worker on one server, with logs and resource graphs close at hand — and Claude Code can find the slow query for you. See how it works or join the waitlist.
Related: 502 Bad Gateway · Why Is My Website Slow? · HTTP Status Codes Explained · Your App Needs Background Jobs
Keep reading
npm run build Fails but npm run dev Works: Why and How to Fix It
Development mode is forgiving; production builds are strict. The common reasons a build fails when dev works — TypeScript and lint errors, case-sensitive imports, missing environment variables, server-only code in the browser, prerendering errors and memory — with the fix for each.
"exec format error" in Docker: ARM vs x86 Images Explained
exec format error almost always means a container image built for one CPU architecture (ARM, like Apple Silicon Macs) is running on another (x86/amd64 servers), or vice versa. How to check, build for the right platform with --platform and buildx, and the other cause: scripts without a shebang or with Windows line endings.