How to Fix HTTP 504 Gateway Timeout in Node.js, Express & FastAPI
A production guide to diagnosing and fixing HTTP 504 Gateway Timeout errors across Nginx, Node.js Express, and Python FastAPI application stacks.
The HTTP 504 Gateway Timeout status code is one of the most frustrating errors in backend engineering. Unlike a 500 Internal Server Error (which typically provides a stack trace in your server logs), a 504 often leaves your application logs completely empty.
Here is the exact diagnostic flow to identify where your request is hanging and how to fix 504 errors across Nginx, Node.js/Express, and Python FastAPI.
1. What a 504 Error Actually Means
An HTTP 504 error occurs when a server acting as a gateway or reverse proxy (e.g., Nginx, Cloudflare, AWS ALB) does not receive a timely response from the upstream application server (e.g., Node.js, Gunicorn/Uvicorn, Go binary).
Client Browser
│ (1) Sends HTTP Request
▼
[Reverse Proxy / Nginx] (Default timeout: 60s)
│ (2) Proxies request
▼
[App Server: Node / FastAPI] ──▶ [Database / External API]
│
│ ⏳ App hangs / query takes 65s...
▼
[Reverse Proxy / Nginx]
│ (3) Timeout reached! Upstream didn't answer in 60s.
▼
Client receives: 💥 HTTP 504 Gateway TimeoutNotice that the error is generated by the reverse proxy, not your backend application. That is why your backend code often shows no unhandled exceptions.
2. Fixing 504 in Nginx (The Proxy Layer)
If your backend legitimately requires more time to process long-running tasks (e.g., generating large PDF reports or processing video uploads), you must increase Nginx's upstream read timeouts.
Open your Nginx configuration file (`/etc/nginx/sites-available/default` or `/etc/nginx/nginx.conf`):
server {
listen 80;
server_name api.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Increase upstream timeouts from default 60s to 300s
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
send_timeout 300s;
}
}Reload Nginx:
sudo nginx -t && sudo systemctl reload nginx> Warning: Increasing proxy timeouts is a temporary fix. If an ordinary CRUD endpoint takes longer than 2 seconds, the issue is an unindexed query or an unhandled async promise.
3. Fixing 504 in Node.js & Express
In Node.js, 504 errors typically happen due to two distinct causes:
Cause A: An Unhandled Async Promise That Never Resolves
If an error occurs inside a try/catch block that doesn't return a response, the connection remains open until the proxy times out:
// ❌ WRONG: Request hangs forever on error
app.post('/api/orders', async (req, res) => {
try {
const order = await processOrder(req.body);
res.status(201).json(order);
} catch (err) {
console.error(err);
// Missing res.status(500).json(...)! Request hangs!
}
});
// ✅ CORRECT: Always ensure a response is returned
app.post('/api/orders', async (req, res, next) => {
try {
const order = await processOrder(req.body);
return res.status(201).json(order);
} catch (err) {
return next(err); // Central error handler sends HTTP response
}
});Cause B: Node.js Default Server Timeout
In newer Node.js releases, `server.headersTimeout` and `server.requestTimeout` default to values that can terminate long-running requests before Nginx does:
const server = app.listen(3000);
server.keepAliveTimeout = 65000; // Keep slightly higher than proxy timeout
server.headersTimeout = 66000;4. Fixing 504 in Python FastAPI & Uvicorn
In FastAPI, 504 errors commonly occur when synchronous blocking calls are placed inside `async def` routes, freezing the entire Uvicorn worker thread:
import time
from fastapi import FastAPI
app = FastAPI()
# ❌ WRONG: time.sleep blocks the entire async event loop!
@app.get("/slow-task")
async def slow_task():
time.sleep(10) # Freezes Uvicorn worker!
return {"status": "done"}
# ✅ CORRECT: Use asyncio.sleep or run in background thread pool
import asyncio
@app.get("/slow-task")
async def fast_task():
await asyncio.sleep(10) # Yields execution back to event loop
return {"status": "done"}When running Uvicorn behind Nginx, configure worker timeout flags:
uvicorn main:app --workers 4 --timeout-keep-alive 755. Continuous Probing to Catch 504s Early
Rather than discovering 504 timeouts from customer complaints, use an automated API monitor.
API Test Lab's Uptime Monitoring pings your core endpoints at configurable intervals (e.g., every 5 minutes) and alerts your team via webhook or email as soon as an endpoint's latency spikes near your gateway timeout threshold.
Frequently Asked Questions
What is the difference between a 502 and a 504 error?
- 502 Bad Gateway: The upstream server crashed or immediately rejected the TCP connection (e.g., app died or port closed).
- 504 Gateway Timeout: The upstream server accepted the connection, but took longer to respond than the proxy's timeout limit.
More from the blog
Read 3 related articles from our latest posts.