DeployEasy
NginxIntermediate

Nginx 502 Bad Gateway with Node.js: An Inside-Out Checklist

Find the cause of an Nginx 502 after deploying Next.js, NestJS, or Node.js with curl, ss, systemd, PM2, Docker Compose, and logs.

· 2 min read· 416 words
Table of contents

An Nginx 502 Bad Gateway means the proxy could not obtain a valid response from its upstream. The browser only shows the final symptom; inspect the request path from Nginx to the Node.js process.

1. Read Nginx’s error log

sudo tail -n 200 /var/log/nginx/error.log
sudo nginx -t
sudo systemctl status nginx

Messages such as connect() failed (111: Connection refused) usually mean nothing is listening at the configured upstream. A timeout points to a hung process, wrong network path, or firewall rule.

2. Test the upstream directly

If Nginx uses port 3000, test it on the VPS:

curl -i http://127.0.0.1:3000/health
sudo ss -ltnp | grep :3000

If this fails, fix Node.js or its container before changing Nginx. If it succeeds, compare the address, port, path, and protocol with proxy_pass.

3. Check PM2 or systemd

pm2 status
pm2 logs myapp --lines 200
systemctl status myapp
journalctl -u myapp --since "30 minutes ago"

Look for a missing environment variable, a failed database connection, an unsupported Node version, a build that never completed, or a process that exits immediately.

4. Check Docker Compose

docker compose ps
docker compose logs --tail=200 api
docker compose exec api wget --spider --quiet http://127.0.0.1:3000/health

When Nginx runs on the host, 127.0.0.1:3000 must be published by the API container. When Nginx runs in Compose, use the service name, such as api:3000, instead of localhost.

A minimal proxy block

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
    }
}

After editing:

sudo nginx -t
sudo systemctl reload nginx

Common causes

  • PM2 or the container is stopped or restarting.
  • The application listens on a different port.
  • The application binds only to an unexpected interface.
  • Nginx uses localhost but the upstream is in another container.
  • The app crashed while connecting to PostgreSQL or Redis.
  • A firewall or provider network rule blocks the upstream path.
  • The Nginx configuration was edited but not reloaded.

Five-minute checklist

  1. Read the Nginx error log.
  2. Run nginx -t.
  3. Check the process or container status.
  4. Curl the upstream directly.
  5. Compare proxy_pass with the actual listener.
  6. Check application logs and environment variables.
  7. Reload Nginx only after the configuration validates.

A 502 is usually resolved faster by proving each hop independently than by repeatedly refreshing the public page.

Continue reading