Nurture TechnologiesNurture Tech
DevOps9 min read·July 16, 2026

How to Fix Nginx 502 Bad Gateway ErrorCauses and Solutions

NginxLinuxSystemd

A 502 Bad Gateway means Nginx is running but the application behind it is not responding. This is the exact workflow we use to diagnose and fix it in under ten minutes.

What Is a 502 Bad Gateway Error?

When Nginx returns a 502, it is telling you: I received your request, tried to forward it to the upstream server, and got either no response or an invalid one. Nginx itself is healthy. The problem is with whatever is sitting behind it, your Node.js app, your Python/Django process, your PHP-FPM pool, your Docker container, or any other upstream service.

This is different from a 503 (service temporarily unavailable, often intentional) or a 504 (the upstream responded but too slowly). A 502 almost always means the upstream process is either not running, not listening on the expected address, or crashing immediately when contacted.

Common Symptoms

  • Browser shows '502 Bad Gateway' with Nginx in the footer
  • curl returns HTTP/1.1 502 Bad Gateway against the public URL
  • Nginx error log shows: connect() failed (111: Connection refused) while connecting to upstream
  • Nginx error log shows: recv() failed (104: Connection reset by peer)
  • The error appeared after a deployment, server restart, or traffic spike
  • Refreshing occasionally works, pointing to a process that crashes and restarts
  • All routes return 502, not just one endpoint

Most Common Causes

1. Application Crashed

The most frequent cause. Your app threw an uncaught exception, ran out of memory, or hit a fatal startup error and exited. Nginx is still running and accepting connections, but there is nothing behind it to forward the request to.

2. Wrong Upstream Configuration

The proxy_pass directive in your Nginx config points to the wrong address or port. The app is running fine, Nginx is just knocking on the wrong door.

3. Service Not Running

The application was never started, or it did not survive a server reboot because it was not configured as a systemd service with auto-restart. Common after infrastructure work or deployments that reset the environment.

4. Port Mismatch

Your app was configured to listen on port 3001 but Nginx's proxy_pass is still pointing to port 3000 from a previous configuration. This is especially common during refactors or when environment variables controlling the port change.

5. Firewall Restrictions

A firewall rule on the server (iptables, ufw) is blocking traffic on the internal port your app uses. Nginx cannot reach the upstream even though both are on the same machine.

6. Resource Exhaustion

The server has run out of memory (OOM killer terminated the app process), file descriptors (too many open connections), or CPU (process unresponsive). The app process either died or is too busy to respond within Nginx's timeout window.

How to Diagnose the Issue

Run these in order. Each command narrows down the cause. You will usually find it within the first three steps.

Step 1, Read the Nginx error log first

The error log almost always names the specific upstream address that failed and the error code. This tells you exactly what Nginx tried to connect to.

$sudo tail -50 /var/log/nginx/error.log

Look for lines containing 'connect() failed', 'no live upstreams', or 'upstream timed out'. The address in brackets (e.g. 127.0.0.1:3000) is what Nginx is trying to reach, note that address for the next steps.

If you have a site-specific log, check /var/log/nginx/your-site-error.log first. The error address from these logs is what you will use to verify with curl in Step 4.

Step 2, Check if your application is running

$sudo systemctl status your-app-service
$ps aux | grep -E 'node|python|php|ruby|gunicorn|uvicorn' | grep -v grep

If systemctl shows 'inactive (dead)' or 'failed', the process is not running. If ps returns nothing, the process has exited. Either way, the cause is clear: the upstream is gone.

Step 3, Verify what is actually listening on the expected port

$sudo ss -tlnp | grep LISTEN
$sudo netstat -tlnp | grep LISTEN

Find the port your app should be using. If it does not appear in the output, nothing is listening there. If a different port appears, you have a port mismatch between the app and the Nginx config.

Step 4, Test the upstream directly with curl

Bypass Nginx entirely and hit the upstream address directly. Use the exact address from the Nginx error log.

$curl -v http://127.0.0.1:3000/

If curl returns 'Connection refused', the port is closed, the app is not running or not listening. If curl returns a valid response, the app is up and the problem is in the Nginx configuration or a network rule. If curl hangs, the process is running but unresponsive (resource exhaustion or deadlock).

Step 5, Check application logs

$sudo journalctl -u your-app-service -n 100 --no-pager
$pm2 logs --lines 100

Look for unhandled exceptions, 'EADDRINUSE' (port already in use), 'ENOMEM' (out of memory), or any fatal error that would have caused the process to exit. The timestamp on the last log line tells you when the process died.

Step-by-Step Fixes

Fix 1, Restart the application

If the app crashed, restart it first to restore service. Then investigate the root cause from the logs before it crashes again.

$sudo systemctl restart your-app-service
$pm2 restart your-app

Restarting without checking why it crashed will get you back online temporarily but the crash will repeat. Always read the logs after restarting to understand the root cause.

Fix 2, Correct the upstream port in the Nginx config

If ss -tlnp shows the app is listening on a different port than what Nginx is configured to use, update the proxy_pass directive to match.

/etc/nginx/sites-available/your-site
server {
    listen 80;
    server_name example.com;

    location / {
        # Change this port to match what your app is actually listening on
        proxy_pass http://127.0.0.1:3000;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;

        proxy_read_timeout 300s;
        proxy_connect_timeout 75s;
        proxy_send_timeout 300s;
    }
}

After editing, always test the config before reloading, a syntax error will take Nginx down entirely.

$sudo nginx -t
$sudo systemctl reload nginx

Fix 3, Run the application as a managed systemd service

If the app is not starting on boot or not restarting after a crash, create a proper systemd service. This is the correct long-term solution for any production server.

/etc/systemd/system/your-app.service
[Unit]
Description=Your Application
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/your-app
ExecStart=/usr/bin/node /var/www/your-app/server.js
Restart=always
RestartSec=5
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=your-app
Environment=NODE_ENV=production
Environment=PORT=3000

[Install]
WantedBy=multi-user.target
$sudo systemctl daemon-reload && sudo systemctl enable your-app && sudo systemctl start your-app

Fix 4, Check and fix firewall rules

If curl from the server itself can reach the upstream but Nginx cannot, a firewall rule is likely blocking internal traffic. Allow the port on loopback.

$sudo ufw status verbose
$sudo iptables -L -n | grep 3000

For ufw, allow traffic on the internal port. Since the upstream is local, this typically should already be open, but an overly strict policy can block it.

$sudo ufw allow from 127.0.0.1 to any port 3000

Fix 5, Increase Nginx worker and file descriptor limits

If the server is under high load and connections are being exhausted, increase the limits in the main Nginx config.

/etc/nginx/nginx.conf
# At the top level (outside any block)
worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    # Upstream connection keep-alive
    upstream your_app {
        server 127.0.0.1:3000;
        keepalive 32;
    }
}

Fix 6, Tune proxy timeout settings

If your application responds correctly to curl but Nginx returns 502, the upstream may be responding slower than Nginx's default timeout allows (60 seconds). For long-running requests, increase the timeout values.

/etc/nginx/sites-available/your-site
location / {
    proxy_pass http://127.0.0.1:3000;

    # How long to wait for the upstream to accept the connection
    proxy_connect_timeout 75s;

    # How long to wait between data reads from the upstream
    proxy_read_timeout 300s;

    # How long to wait to send data to the upstream
    proxy_send_timeout 300s;
}

Do not blindly increase timeouts to very high values. A genuinely slow upstream is itself a problem. Increase timeouts only if you have verified the upstream response time and it is legitimately long (e.g., AI inference, video processing, report generation).

Verification Checklist

Run through these after applying the fix before declaring the issue resolved.

  • Nginx config passes syntax check: sudo nginx -t returns 'syntax is ok'
  • Application process is running: systemctl status shows 'active (running)'
  • Port is listening: ss -tlnp shows the expected port with the app's PID
  • Upstream responds directly: curl http://127.0.0.1:PORT/ returns a valid response
  • Public URL is returning 200: curl -I https://example.com/ returns HTTP/2 200
  • Nginx error log is clean: tail /var/log/nginx/error.log shows no new upstream errors
$curl -o /dev/null -s -w '%{http_code}\n' https://example.com/

That command should return 200. Anything else means the fix is not complete.

Prevention Best Practices

Use a process manager with auto-restart

Never run a production application with a bare node server.js or python app.py command in a terminal. Use systemd (shown above) or PM2 with the --watch flag. Configure Restart=always in systemd or pm2 startup so the process automatically recovers from crashes.

Monitor the upstream independently

Set up a health check endpoint in your application (e.g., GET /health returning 200) and configure an uptime monitor against it. Tools like UptimeRobot, Better Uptime, or a self-hosted Uptime Kuma instance alert you before users start reporting 502 errors.

Use Nginx upstream health checks

The Nginx Plus upstream health check feature (or the open-source nginx_upstream_check_module) actively monitors upstream health and marks servers down before sending real traffic to them. This does not prevent crashes but reduces the blast radius when they happen.

Log rotation and disk space monitoring

A full disk is a non-obvious cause of 502 errors, applications fail to write logs or temp files and crash. Set up logrotate for your application logs and monitor disk usage with an alert at 80% capacity.

$df -h && du -sh /var/log/nginx/*

Pin your port configuration with environment variables

Use an environment variable (PORT) that both your application and your Nginx configuration reference from the same source of truth. When the port changes, it changes in one place. Port mismatches are almost always caused by manual edits that only update one side.

Final Thoughts

A 502 Bad Gateway almost always has a simple, immediate cause: the upstream process crashed, was never started, or is being contacted on the wrong address. The diagnostic workflow is fast, five commands, read in order, will identify the cause in under five minutes in the vast majority of cases.

The harder problem is why it happened and whether it will happen again. A crash with no process manager means it will happen every time the server reboots. A port mismatch will happen every time that configuration file is touched. A resource exhaustion issue will happen again at the next traffic spike.

Fix the immediate 502 first, then spend fifteen minutes addressing the root cause. The five-minute fix that prevents the next three hours of downtime is always worth it.

Nurture Technologies

NEED HELP WITH YOUR STACK?

Nurture Technologies builds and maintains production-quality software for startups and businesses. If engineering problems are slowing you down, our team can help.

Talk to our team →
FAQ

FREQUENTLY ASKED QUESTIONS

What causes a 502 Bad Gateway in Nginx?+

A 502 means Nginx successfully received a request but the upstream server (your application) either refused the connection, returned an invalid response, or did not respond at all. The most common causes are: the application process has crashed or is not running, the proxy_pass directive in the Nginx config points to the wrong port, a firewall rule is blocking the connection on the internal port, or the server has run out of memory or file descriptors and the application is unresponsive.

How do I check Nginx error logs?+

Run: sudo tail -50 /var/log/nginx/error.log, this shows the 50 most recent error entries. For site-specific logs, check /var/log/nginx/your-site-error.log. The error message will include the upstream address that failed and the specific error code (111 = connection refused, 104 = connection reset). These two pieces of information tell you exactly what Nginx tried to connect to and why it failed.

What is the difference between a 502 and a 503 error?+

502 Bad Gateway means Nginx tried to reach the upstream and got an invalid or no response, the upstream is broken or unreachable. 503 Service Unavailable typically means Nginx intentionally cannot serve the request, often because all upstream servers are marked down (in a load balancing setup) or because a maintenance mode is active. 504 Gateway Timeout is a close relative of 502: the upstream responded eventually, but too slowly, Nginx gave up waiting.

How do I restart Nginx after fixing the configuration?+

Always test the configuration first with: sudo nginx -t, this catches syntax errors before they take the server down. Then reload (not restart) with: sudo systemctl reload nginx, this applies the configuration change without dropping existing connections. Only use sudo systemctl restart nginx if reload does not work or you need to fully reset the worker processes.

Can a firewall cause a 502 error?+

Yes. If a firewall rule on the server blocks traffic on the internal port your application uses, Nginx cannot reach the upstream even though both processes are on the same machine. Check with: sudo ufw status or sudo iptables -L -n. Allow loopback traffic on your app's port: sudo ufw allow from 127.0.0.1 to any port 3000. This is more common on servers with strict default-deny firewall policies.

Why does my 502 error come and go intermittently?+

Intermittent 502 errors typically point to one of three causes: the application is crashing and restarting repeatedly (check the logs for crash timestamps that correlate with 502 occurrences), the server is under high load and occasionally exhausting connections or memory, or you have multiple upstream instances and one of them is unhealthy. Run: sudo journalctl -u your-app -n 200 and look for restart events, if the app is restarting every few minutes, that is the cause.