AdminsForum

Web servers

Guide How to Fix Nginx 502 Bad Gateway — Complete Troubleshooting Guide

Started by DEV 0 replies 103 views
1DEV
#1
A 502 Bad Gateway means a gateway did not receive a usable response from its upstream. Fix the failed link in the request path rather than repeatedly restarting the whole server.

Scope: This guide covers Nginx in front of PHP-FPM or an HTTP application on Linux. Commands assume a conventional systemd installation. Panel-managed Nginx may have different binaries, paths and service names; use the panel's supported configuration tools and logs. Back up the affected configuration before editing. Never share unredacted nginx -T output: configuration can contain secrets.

1. Identify which layer returns the error
Write down the failing URL, time, request method and whether all pages fail or only one application. A CDN, load balancer, Nginx or the application itself can return 502. Response branding and headers are clues, not proof. Correlate the same request with each layer's logs.

For a harmless public endpoint:
Code:
curl -sS -D - -o /dev/null https://example.com/health
Use a real, safe URL; do not replay payments or other state-changing requests. If authorized to test the origin directly, preserve the hostname and TLS validation:
Code:
curl --resolve example.com:443:YOUR_ORIGIN_IP -sS -D - -o /dev/null https://example.com/health
Do not add -k to hide a certificate problem. An origin restricted to the CDN may correctly reject your workstation.

2. Read the matching error log
Find the error_log directive for the affected virtual host. /var/log/nginx/error.log is a common package default, not a universal path:
Code:
sudo tail -n 100 /var/log/nginx/error.log sudo journalctl -u nginx --since "15 minutes ago" --no-pager sudo nginx -t
Inspect nginx -T locally if you need to locate the active server block, proxy_pass or fastcgi_pass. Ensure you are using the binary/configuration that the running service actually uses. Note the upstream address and exact error.
  • Connection refused: The target port/socket has no accepting service, or a device actively rejects it. Check process health and listener address.
  • No such file or directory: A Unix socket path is wrong or the expected socket was never created.
  • Permission denied: Check socket and parent-directory access plus SELinux/AppArmor denials.
  • Upstream prematurely closed connection: Investigate application crashes, worker termination and backend logs.
  • Upstream sent invalid header / too big header: Inspect backend output, cookies and response headers. Protocol mismatch or oversized headers needs a specific fix.
  • Upstream timed out: Investigate slow or overloaded dependencies. This often produces 504; error-page mappings and intermediate gateways can change the displayed status.

3. Check backend health and capacity
Code:
sudo ss -lntp sudo ss -lxnp systemctl --failed free -h df -h df -i sudo journalctl -k --since "30 minutes ago" --no-pager
Look for an OOM kill, exhausted disk/inodes, a crash loop or a deployment at the same timestamp. Confirm the backend's service name before inspecting its status and journal. Restart only the affected service after capturing evidence and understanding the impact; repeated restarts can hide the cause and interrupt healthy requests.

4. PHP-FPM: make the listener and fastcgi_pass agree
A PHP version change commonly leaves Nginx pointing at an old socket. Discover your installed FPM units instead of assuming a PHP version:
Code:
systemctl list-units --all --type=service 'php*-fpm.service' ls -l /run/php/
Inspect the selected FPM pool's listen setting and the site's fastcgi_pass. These are alternative examples, not values to paste blindly:
Code:
# Unix socket example fastcgi_pass unix:/run/php/php8.3-fpm.sock; # TCP listener example fastcgi_pass 127.0.0.1:9000;
Use the actual path/address, and keep FPM on a private interface. Port 9000 is FastCGI, not HTTP: curl to it is not a valid application health check. Verify that the Nginx worker identity can traverse the socket's parent directories and access the socket. Correct FPM listen.owner, listen.group and listen.mode as appropriate for your deployment; a manual chmod may disappear when FPM recreates the socket.

Do not use chmod 777 or disable SELinux/AppArmor. If discretionary permissions look correct, inspect security-policy denials. If FPM logs show pm.max_children saturation, measure worker memory and request duration before changing limits. More workers can trigger OOM on a small VPS. Also inspect database latency and slow requests.

5. HTTP applications: test the configured endpoint locally
For an application intentionally listening on loopback TCP 3000:
Code:
curl --max-time 10 -sS -D - -o /dev/null http://127.0.0.1:3000/health
Replace the port/path with the real endpoint. If it requires a Host header, include the expected hostname. If this fails, fix the backend before editing Nginx. If it succeeds, compare the endpoint with proxy_pass and check whether the backend expects HTTP or HTTPS. Preserve certificate validation and configure upstream SNI/trust correctly for HTTPS.

Inside a container, 127.0.0.1 means that container, not another container or the host. Verify service DNS names, networks and listeners from the Nginx container's context. Avoid publishing an internal application port to the Internet as a troubleshooting shortcut.

6. Check changes and limits without guesswork
If the backend is reachable but only large/login responses fail, inspect the exact header/buffer error and backend response. Fix oversized cookies or abnormal headers first. Change proxy/FastCGI buffer directives only for the relevant site with a measured requirement.

If requests are slow, inspect application, database and external API timing before raising proxy_read_timeout or fastcgi_read_timeout. Those read timeouts concern inactivity between reads, not a universal total-request deadline. Raising them cannot repair a dead listener and may keep scarce workers occupied longer. Prefer background jobs for long-running work.

7. Apply one fix and verify it
After a backed-up configuration edit:
Code:
sudo nginx -t
Only if the test succeeds, reload using the deployment's supported mechanism. On a conventional package installation:
Code:
sudo systemctl reload nginx
Test the affected endpoint and a normal page again, check the logs for fresh errors and watch the backend under realistic traffic. A successful syntax check does not prove the upstream works. If the change fails, restore the saved configuration, validate it and reload. On panels, keep persistent changes in the supported template/settings so regeneration does not overwrite them.

When asking for help, include: OS, panel/container details, Nginx/PHP/app versions, the relevant redacted error line, whether static/PHP/all routes fail, the configured upstream type and the last change before failure. Remove tokens, private keys, credentials and user data from logs/configuration.

Official references
Reply

Users browsing this thread: 1 Guest(s)
Forum Jump: