HTTP 504 Gateway Timeout — upstream server didn't respond in time
Gateway Timeout — upstream server took too long to respond
Verified against RFC 9110 §15.6.5, MDN Web Docs — 504 Gateway Timeout, Nginx proxy_read_timeout docs · Updated June 2026
> quick_fix
HTTP 504 means a reverse proxy, load balancer, or CDN forwarded your request to a backend server, but the backend didn't respond before the proxy's timeout expired. The backend is alive (otherwise you'd get 502 or 503) but too slow. Fix by identifying the slow operation (database query, external API call, file processing), optimizing it, or increasing the proxy timeout if the operation legitimately takes longer than the default.
# Check Nginx proxy timeout settings
grep -r 'proxy_read_timeout\|proxy_connect_timeout' /etc/nginx/
# Increase Nginx upstream timeout (default is 60s)
# proxy_read_timeout 120s;
# proxy_connect_timeout 10s;
# proxy_send_timeout 120s;
# Test if backend responds within timeout
time curl -sI http://localhost:3000/slow-endpoint
# Check for slow database queries (PostgreSQL)
psql -c "SELECT pid, now() - pg_stat_activity.query_start AS duration, query
FROM pg_stat_activity WHERE state != 'idle'
ORDER BY duration DESC LIMIT 5;"What causes this error
A 504 is generated by an intermediary (reverse proxy, load balancer, CDN, API gateway) when it forwarded the request to an upstream server but didn't receive a complete response within its configured timeout. The upstream server is reachable and accepted the connection (otherwise the intermediary would return 502 or 503), but the response is taking too long. Common causes: a database query scanning millions of rows without an index, an external API call to a third-party service that is slow or hanging, a file upload/processing operation that exceeds the proxy timeout, DNS resolution delays between the proxy and upstream, or the upstream event loop is blocked by synchronous computation.
How to fix it
- 01
step 1
Identify which layer is generating the 504
The 504 comes from the intermediary, not the backend. Check response headers to identify who timed out — Nginx, Cloudflare, AWS ALB, or your API gateway. This tells you which timeout to adjust.
# Check response headers for the intermediary curl -sI https://example.com/slow-endpoint # Look for: # Server: nginx -> Nginx proxy_read_timeout # Server: cloudflare -> Cloudflare 100s timeout (not configurable on free plan) # Via: 1.1 ... (CloudFront) -> CloudFront origin response timeout # X-Amzn-ErrorType -> API Gateway 29s hard limit - 02
step 2
Find the slow operation on the backend
The backend is processing something that takes longer than the proxy allows. Find what's slow — it's usually a database query, an external API call, or a file operation.
# Check for slow queries (PostgreSQL) psql -c "SELECT pid, now() - query_start AS duration, substring(query, 1, 100) FROM pg_stat_activity WHERE state = 'active' ORDER BY duration DESC LIMIT 10;" # Check for slow queries (MySQL) mysql -e "SHOW PROCESSLIST;" | grep -v Sleep # Check for hanging external API calls netstat -an | grep ESTABLISHED | grep -c ':443' - 03
step 3
Optimize the slow operation or increase the timeout
If the operation can be made faster (add a database index, cache the result, paginate), do that first. If it legitimately takes 2+ minutes (report generation, large file processing), increase the proxy timeout and consider making it asynchronous.
# Nginx — increase timeout for a specific slow endpoint location /api/reports { proxy_pass http://backend; proxy_read_timeout 180s; proxy_connect_timeout 10s; proxy_send_timeout 180s; } # AWS ALB — increase idle timeout (default 60s, max 4000s) aws elbv2 modify-load-balancer-attributes \ --load-balancer-arn arn:aws:... \ --attributes Key=idle_timeout.timeout_seconds,Value=120 - 04
step 4
Make long operations asynchronous
For operations that genuinely take minutes (PDF generation, data exports, video processing), don't make the client wait. Accept the request, return a 202 Accepted with a job ID, and let the client poll for completion.
// Instead of synchronous processing that triggers 504: // app.post('/api/export', async (req, res) => { // const data = await generateLargeExport(); // takes 3 min // res.json(data); // 504 before this line runs // }); // Use async job pattern: app.post('/api/export', async (req, res) => { const jobId = crypto.randomUUID(); queue.add('export', { jobId, ...req.body }); res.status(202).json({ jobId, status: 'processing', poll: `/api/export/${jobId}/status` }); }); app.get('/api/export/:id/status', async (req, res) => { const job = await queue.getJob(req.params.id); if (job.isCompleted) return res.json({ status: 'done', url: job.result }); res.json({ status: 'processing', progress: job.progress }); });
How to verify the fix
- The previously-504ing endpoint now returns a response within the proxy timeout.
- Slow database queries have been indexed or paginated (check with EXPLAIN ANALYZE).
- Long-running operations use the async job pattern and return 202 immediately.
- Nginx error.log shows no new 'upstream timed out' entries.
Why 504 happens at the runtime level
At the HTTP protocol level, 504 is exclusively generated by intermediaries — reverse proxies, load balancers, CDNs, and API gateways. The intermediary opened a connection to the upstream, sent the request, and started a timer. When the timer expired without receiving response headers, it generated the 504 and sent it back to the client. The upstream server itself never sends a 504; it either sends a response or doesn't. The key diagnostic insight: the timeout value is configured on the intermediary, not the backend. Nginx defaults to 60 seconds (proxy_read_timeout), AWS ALB defaults to 60 seconds (idle timeout), Cloudflare defaults to 100 seconds (non-configurable on free plans), and AWS API Gateway has a hard 29-second limit.
Common debug mistakes for 504
- Increasing the proxy timeout without investigating why the backend is slow — this masks the real problem and shifts the 504 to a worse user experience (waiting 3 minutes for a response instead of 1 minute).
- Increasing the backend timeout without also increasing the proxy timeout — if your database query timeout is 120s but Nginx proxy_read_timeout is 60s, you'll still get a 504 at 60 seconds.
- Forgetting that timeout chains must be ordered correctly: client > proxy > backend > database. If any link in the chain has a shorter timeout than the one behind it, you'll get premature 504s.
- Not checking all intermediaries — a request might pass through CDN, load balancer, reverse proxy, then app, and the 504 can come from any of these layers. Check all timeout values in the chain.
- Assuming the backend server is the problem when the actual cause is DNS resolution between the proxy and upstream taking too long (especially with service discovery in Kubernetes).
When 504 signals a deeper problem
Intermittent 504s under normal load usually signal one of two architectural issues. First: an N+1 query pattern where a single API request triggers hundreds of sequential database queries that collectively exceed the timeout, even though each individual query is fast. This gets worse as data grows. Second: a synchronous dependency on an external API with no timeout or circuit breaker — when that third-party API has a bad day and responds in 45 seconds instead of 200 milliseconds, every request through your server stacks up waiting, workers get exhausted, and 504s cascade. The fix isn't increasing timeouts — it's adding circuit breakers, query batching, and caching so your server's response time doesn't depend on the performance of systems you don't control.
Editor's take
504 is the error that teaches you about timeout chains. Every production system has a chain of timeouts: client, CDN, load balancer, reverse proxy, application, database (or external API). A 504 fires when any link in that chain gives up waiting for the link behind it.
The mistake I see most often: someone gets a 504, increases Nginx's proxy_read_timeout from 60s to 300s, and calls it fixed. Now users wait 5 minutes for a blank page instead of 1 minute. The real fix is making the backend faster — add the missing database index, cache the expensive computation, or make the operation asynchronous.
The most insidious 504 pattern is the API Gateway hard limit. AWS API Gateway REST APIs have a non-negotiable 29-second timeout. No amount of configuration changes it. I've watched teams spend days trying to 'fix' their API Gateway 504s before discovering this limit exists. If your Lambda needs more than 29 seconds, you need a different architecture (SQS + polling, WebSocket API, or ALB instead of API Gateway).
Another lesson: 504s are the canary in your performance mine. If you're getting occasional 504s at normal traffic, your system is already at its limit. The next traffic spike won't produce 'more 504s' — it'll produce a full outage. Treat every 504 as a capacity warning, not just an incident.
By Bikram Nath · Curator · Updated June 2026
Frequently asked questions
What's the difference between HTTP 502 and 504?
502 Bad Gateway means the proxy received an invalid or incomplete response from the upstream (connection reset, malformed headers, process crash mid-response). 504 Gateway Timeout means the proxy received no response at all within its timeout window — the upstream is still processing. 502 = upstream broke; 504 = upstream is too slow.
Can I fix a 504 on Cloudflare's free plan?
Cloudflare's free and Pro plans have a 100-second origin response timeout that cannot be increased. If your backend takes longer than 100 seconds, you have three options: make the backend faster, upgrade to Cloudflare Enterprise (which allows custom timeouts), or bypass Cloudflare for that specific endpoint by using a grey-clouded DNS record.
Does API Gateway have a 504 timeout limit?
AWS API Gateway (REST) has a hard 29-second integration timeout that cannot be increased. If your Lambda or backend needs more than 29 seconds, use API Gateway WebSocket APIs (which support up to 2 hours), or switch to ALB + Lambda (which supports up to 15 minutes). This 29-second limit is the most common source of API Gateway 504s.