ConfigClarity

Free browser-based DevOps audit tools โ€” no signup, nothing leaves your browser

nginx 502 Bad Gateway

Upstream not responding โ€” 5 causes and their fixes

502 means: nginx reached the upstream server address but got no valid response. The most common cause in Docker setups is using localhost instead of the container service name.

Cause 1 โ€” proxy_pass to localhost (most common)

Inside a Docker container, localhost refers to the container itself โ€” not other containers. Use the Docker service name instead.

โŒ Wrong โ€” localhost resolves to the nginx container itself
location / {
    proxy_pass http://localhost:3000;
}
โœ… Fixed โ€” use the Docker service name
location / {
    proxy_pass http://app:3000;  # "app" = service name in compose
}

Cause 2 โ€” Container not on the same network

Docker containers can only reach each other by service name if they're on the same network. By default, services in the same compose file share a network โ€” but if nginx is in a separate compose file, you need an explicit shared network.

โœ… Shared network in docker-compose.yml
services:
  nginx:
    image: nginx
    networks:
      - webnet
  app:
    image: myapp
    networks:
      - webnet

networks:
  webnet:

Cause 3 โ€” App not listening on correct port

Verify what port your application is actually listening on inside the container:

# Check what ports are open inside the container
docker exec -it your_app_container ss -tlnp

# Check application logs
docker logs your_app_container --tail 50

Cause 4 โ€” Container not running or crashed

# Check container status
docker ps -a

# View crash logs
docker logs your_app_container --tail 100

# Restart and watch
docker compose up app --force-recreate

Cause 5 โ€” Missing proxy headers

Some apps require specific headers to function correctly behind a proxy:

โœ… Complete proxy_pass block
location / {
    proxy_pass http://app: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;
}

Audit your nginx.conf or Traefik labels

The Reverse Proxy Mapper detects proxy_pass to localhost, dangling routes, missing SSL redirects, and Traefik v3 breaking changes automatically.

Open Reverse Proxy Mapper โ†’

Frequently Asked Questions

How do I find the correct service name to use in proxy_pass?

The service name is the key under services: in your docker-compose.yml. For example, if your compose has services: backend: image: myapp, use proxy_pass http://backend:PORT.

Why does it work locally but not in production?

Local setups often run nginx directly on the host where localhost works. In production with containerised nginx, localhost refers to the nginx container. Always use service names in Docker environments.

How do I validate my nginx config before restarting?

Run docker exec your_nginx_container nginx -t to test the configuration without restarting. If valid, run docker exec your_nginx_container nginx -s reload to apply changes without downtime.

What's the difference between 502 and 504?

502 means nginx connected to the upstream but received an invalid response (or the connection was refused). 504 means nginx connected but the upstream took too long to respond. 502 is usually a configuration issue; 504 is usually an application performance issue.