Where to Look When a Reverse Proxy Returns 502
A 502 tells you a gateway could not get a valid response from an upstream. With Nginx in front of a web app, the fastest way to narrow it down is to make the same request from the network Nginx uses.
1. Reproduce the failing request and read the log
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' \
--max-time 10 https://app.example.com/health
Use the path that actually fails. Check the proxy error log at the time of your request. If Nginx is a Compose service called proxy and logs to the container stream, use:
docker compose logs --since 5m proxy
If there is no corresponding log entry, check whether the response came from another gateway in front of Nginx. If there is an entry, note the upstream hostname, port, and error message.
2. Test the upstream directly
Check the active proxy_pass configuration for the scheme and destination. From the proxy’s network, try the effective upstream URL. In this example, app is a Compose service listening on port 8080, and the upstream health route is /health.
docker compose exec proxy curl -sv --connect-timeout 3 \
--max-time 10 http://app:8080/health
If the proxy image lacks curl, use an available HTTP client or a diagnostic container on the same network.
| Result | What to check |
|---|---|
| Name cannot resolve | The upstream name and shared Docker network |
| Connection refused | The app process, its listening address, and the internal port |
| Connection times out | Routing or firewall rules between the proxy and app |
| TLS handshake or certificate error | The upstream scheme, hostname, and certificate trust |
| Expected response | The proxied path, Host header, and any differences between the direct request and the failing one |
3. Apply the smallest fix and repeat both tests
If you changed an Nginx configuration file, run nginx -t in the environment where Nginx runs before reloading. Repeat the upstream request and then the public request that originally failed. Check the status and response content so you know the public URL is reaching the intended app.