A 502 Bad Gateway error occurs when a reverse proxy server (like nginx or a load balancer) receives an invalid or no response from your Node.js application. This typically happens when your app crashes, times out, or fails to respond properly to incoming requests. Fixing this requires identifying why the upstream Node.js server is failing to respond.
The 502 Bad Gateway error is an HTTP status code indicating that a server acting as a gateway or proxy received an invalid response from the upstream server. In Node.js deployments, this means your reverse proxy (nginx, Apache, or a cloud load balancer) tried to forward a request to your Node.js application, but the Node.js server either did not respond, crashed, responded with invalid data, or took too long to respond. The proxy server then returns this 502 error to the client because it cannot complete the request. This is distinct from a 504 Gateway Timeout, which specifically indicates the upstream server did not respond in time, whereas 502 can mean various types of invalid or missing responses.
First, check if your Node.js process is actually running. If you use PM2, run:
pm2 statusFor systemd services:
sudo systemctl status your-app-nameIf the app is stopped or crashed, restart it:
pm2 restart app-name
# or
sudo systemctl restart your-app-nameIf the app keeps crashing, check the application logs to find the root cause.
Review logs to identify crashes or errors. For PM2:
pm2 logs app-name --lines 100For systemd:
sudo journalctl -u your-app-name -n 100 --no-pagerLook for uncaught exceptions, memory errors, or stack traces that indicate why the application crashed or stopped responding.
Ensure your Node.js app is listening on the port that your reverse proxy is forwarding to. Check your nginx configuration (usually in /etc/nginx/sites-available/):
location / {
proxy_pass http://localhost:3000; # Must match your Node.js port
}Verify your Node.js app is listening on that port:
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});Check which ports are in use:
sudo netstat -tlnp | grep nodeTry accessing your Node.js application directly to determine if the issue is with the proxy or the app itself:
curl http://localhost:3000If this works but accessing via the domain fails, the problem is with the reverse proxy configuration. If this also fails, the issue is with your Node.js application.
If your Node.js app takes time to process requests (database queries, API calls), increase the timeout values in your nginx configuration:
location / {
proxy_pass http://localhost:3000;
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
proxy_send_timeout 300s;
}Test the configuration and reload nginx:
sudo nginx -t
sudo systemctl reload nginxMonitor your application's memory usage. If memory grows continuously, you have a memory leak:
pm2 monit
# or
top -p $(pgrep node)If memory usage is consistently high, increase the Node.js heap limit:
node --max-old-space-size=4096 app.jsFor PM2:
// ecosystem.config.js
module.exports = {
apps: [{
name: "app",
script: "./app.js",
node_args: "--max-old-space-size=4096"
}]
};Ensure your Node.js application handles errors gracefully:
// Catch uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
// Log to monitoring service
process.exit(1); // Let PM2/systemd restart
});
// Catch unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1);
});
// Graceful shutdown
process.on('SIGTERM', async () => {
console.log('SIGTERM received, closing server...');
await server.close();
process.exit(0);
});Use a process manager to automatically restart your app if it crashes. For PM2:
pm2 start app.js --name my-app --max-restarts 10For systemd, ensure restart is configured:
# /etc/systemd/system/your-app.service
[Service]
Restart=always
RestartSec=10Reload and restart:
sudo systemctl daemon-reload
sudo systemctl restart your-appIn production environments, 502 errors often occur during high traffic periods when the Node.js event loop becomes blocked or the application runs out of memory. Consider implementing horizontal scaling with multiple Node.js instances behind a load balancer using PM2 cluster mode or container orchestration. Monitor key metrics like event loop lag, memory usage, and response times using tools like PM2, New Relic, or DataDog. For applications deployed on cloud platforms (AWS, Google Cloud), check if the load balancer health checks are properly configured and that your Node.js app responds correctly to health check endpoints. Protocol mismatches are a common gotcha: if nginx is configured to use HTTP but your Node.js app only accepts HTTPS, or vice versa, you will get 502 errors. Large request headers (common with JWT tokens or many cookies) may exceed nginx default limits; increase client_header_buffer_size and large_client_header_buffers if needed. For WebSocket applications, ensure both the proxy and Node.js app have appropriate timeout settings and upgrade headers are properly configured.
Error: EMFILE: too many open files, watch
EMFILE: fs.watch() limit exceeded
Error: Middleware next() called multiple times (next() invoked twice)
Express middleware next() called multiple times
Error: Worker failed to initialize (worker startup error)
Worker failed to initialize in Node.js
Error: EMFILE: too many open files, open 'file.txt'
EMFILE: too many open files
Error: cluster.fork() failed (cannot create child process)
cluster.fork() failed - Cannot create child process