How to Fix Nginx 502 Bad Gateway: A Complete Troubleshooting Guide
Experiencing an Nginx 502 Bad Gateway or 504 Timeout? Learn how to diagnose and fix upstream connection refused, reset, and SSL handshake errors effectively.
- A 502 Bad Gateway error occurs when Nginx, acting as a reverse proxy, receives an invalid response or no response from the upstream server (e.g., Node.js, PHP-FPM, Gunicorn).
- The most common root cause is the upstream service crashing, restarting, or not running at all, leading to a '111: Connection refused' error in the Nginx logs.
- Misconfigurations in 'proxy_pass' or 'fastcgi_pass' directives, specifically mismatching ports or Unix socket paths, frequently trigger 502 errors.
- 504 Gateway Timeouts happen when the upstream takes too long to respond, often requiring adjustments to 'proxy_read_timeout' or optimizing backend performance.
- SSL Handshake failures between Nginx and upstream servers usually stem from expired certificates, missing CA trust, or mismatched TLS protocols.
| Method | When to Use | Time | Risk |
|---|---|---|---|
| Checking Nginx Error Logs | Always the first step for 502/503/504 to get the exact failure reason. | 1-2 mins | None |
| Restarting Upstream Services | When logs show 'Connection refused' indicating the backend is down. | 1-3 mins | Low (Service is already down) |
| Increasing Timeout Values | When facing 504 Gateway Timeouts or 'Connection timed out' in logs. | 5 mins | Medium (Can mask underlying performance issues) |
| Fixing Socket/Port Configs | When logs show 'No such file or directory' for sockets. | 5-10 mins | Medium (Requires Nginx reload) |
Understanding the Nginx 502 Bad Gateway Error
When Nginx is configured as a reverse proxy or load balancer, it acts as a middleman between the client (user's browser) and the upstream backend server (such as a Node.js application, Python Gunicorn/uWSGI, or PHP-FPM). A 502 Bad Gateway error indicates that Nginx attempted to communicate with the upstream server, but the upstream server either rejected the connection, abruptly dropped it, or returned an invalid response.
While the 502 error is the most common, it belongs to a family of proxy-related status codes that share similar diagnostic paths:
- 502 Bad Gateway: The upstream server refused the connection or returned an invalid response.
- 503 Service Unavailable: The server is temporarily unable to handle the request, often due to planned maintenance, rate limiting, or resource exhaustion.
- 504 Gateway Timeout: Nginx successfully connected to the upstream, but the upstream failed to return a complete response within the configured timeout window.
Step 1: The Golden Rule - Check the Error Logs
Before making any configuration changes, you must consult the Nginx error logs. The access log will only show the HTTP 502 status, but the error log will provide the exact low-level socket or network error.
Run the following command to tail the error log:
tail -n 50 -f /var/log/nginx/error.log
You are looking for log entries that look like this:
Example 1: Connection Refused
2023/10/26 14:32:10 [error] 12345#0: *6789 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.1.50, server: example.com, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:3000/"
Example 2: Unix Socket Missing
2023/10/26 14:35:22 [error] 12345#0: *6792 connect() to unix:/var/run/php/php8.1-fpm.sock failed (2: No such file or directory) while connecting to upstream
Example 3: Connection Timed Out
2023/10/26 14:40:05 [error] 12345#0: *6801 upstream timed out (110: Connection timed out) while reading response header from upstream
Step 2: Diagnosing 'Connection Refused' (111)
The (111: Connection refused) error is the most frequent cause of a 502 Bad Gateway. It simply means Nginx tried to open a TCP connection to the upstream IP and port (e.g., 127.0.0.1:3000), but nothing is listening there.
Root Causes & Fixes:
- The upstream service is dead/crashed: Check the status of your backend application. If you are running a Node.js app via PM2 or systemd, verify it's active:
systemctl status my-node-apporpm2 status. If it crashed, restart it and check its specific logs to find out why it died. - The upstream service is booting up: If you just deployed new code or restarted the server, the backend might take a few seconds to bind to the port. If requests hit Nginx during this window, they get a 502.
- Wrong Port Configuration: Nginx is configured to
proxy_pass http://127.0.0.1:3000;, but your application is actually listening on port8080. Verify the listening ports using:ss -tulpn | grep LISTEN. - Localhost vs. IP Binding: Nginx is trying to hit
127.0.0.1, but your dockerized app or backend is bound specifically to a public IP or a different network interface (like0.0.0.0).
Step 3: Diagnosing Unix Socket Issues
If you use PHP-FPM or Gunicorn over Unix sockets (e.g., fastcgi_pass unix:/run/php/php8.1-fpm.sock;), 502 errors often stem from file system issues rather than network issues.
Root Causes & Fixes:
- Socket file doesn't exist: The backend service isn't running, or it's configured to listen on a TCP port instead of a socket. Check your FPM pool config (
/etc/php/8.1/fpm/pool.d/www.conf) and ensure thelistendirective matches Nginx exactly. - Permissions issues: Nginx (usually running as
www-dataornginxuser) does not have read/write permissions to the socket file. The Nginx error log will show(13: Permission denied). Ensure the upstream service creates the socket with the correct ownership (e.g.,listen.owner = www-dataandlisten.group = www-datain PHP-FPM).
Step 4: Resolving Nginx 504 Gateway Timeout
A 504 Gateway Timeout occurs when Nginx connects to the upstream successfully, sends the request, but the upstream application takes too long to generate the response. Nginx eventually gives up and drops the connection to prevent tying up worker processes.
How to Fix:
Optimize the Backend: This is the ideal solution. A request taking 60+ seconds is usually a symptom of a missing database index, an external API hanging, or an infinite loop in the code.
Increase Nginx Timeouts: If the long request is intentional (e.g., generating a massive PDF report), you need to tell Nginx to wait longer. Add or increase the following directives in your
locationblock:location /report/ { proxy_pass http://backend; proxy_connect_timeout 60s; proxy_send_timeout 300s; proxy_read_timeout 300s; }Note: For FastCGI (PHP), use
fastcgi_read_timeout 300s;instead.
Step 5: Fixing Connection Reset by Peer (104)
The Nginx log error recv() failed (104: Connection reset by peer) means the upstream server forcefully closed the TCP connection while Nginx was still waiting for data.
This usually indicates severe resource exhaustion on the backend. For example, if a PHP-FPM child process hits its memory limit or execution time limit, the master process will brutally kill it. When the process dies, the socket is torn down, and Nginx receives a TCP RST packet. You must investigate the memory limits and error logs of the backend application itself (e.g., /var/log/php8.1-fpm.log).
Step 6: Nginx SSL Handshake Failed
When Nginx acts as a reverse proxy over HTTPS (e.g., proxy_pass https://backend.internal;), it must negotiate an SSL/TLS connection with the upstream.
Errors like SSL_do_handshake() failed (SSL: error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure) occur due to:
- SNI Mismatch: Nginx needs to send the Server Name Indication so the upstream knows which certificate to present. Add
proxy_ssl_server_name on;to your location block. - Untrusted Upstream Certificate: If the upstream uses a self-signed certificate, Nginx will reject it by default if verification is enabled. You can either provide the CA cert using
proxy_ssl_trusted_certificate /path/to/ca.crt;or, less securely, disable verification (if operating on a trusted internal network). - Protocol Mismatch: Nginx and the upstream share no common TLS protocols or ciphers (e.g., the upstream only supports TLS 1.3, but Nginx proxy settings are restricting it to TLS 1.2).
Frequently Asked Questions
# 1. Tail the Nginx error log to identify the exact cause (Connection refused, timeout, etc.)
tail -n 50 -f /var/log/nginx/error.log
# 2. Check if your backend service is actively listening on the expected port (e.g., 3000)
sudo ss -tulpn | grep :3000
# 3. Check the status of your upstream systemd service (e.g., Node.js, PHP-FPM, Gunicorn)
sudo systemctl status my-backend-service
# 4. If the service is down, attempt to restart it and check its specific logs
sudo systemctl restart my-backend-service
journalctl -u my-backend-service --no-pager | tail -n 20
# 5. After adjusting Nginx timeouts or proxy settings, always test the config before reloading
sudo nginx -t
# 6. If the config test passes, smoothly reload Nginx to apply changes
sudo systemctl reload nginxError Medic Editorial
The Error Medic Editorial team consists of senior DevOps engineers and Site Reliability Experts dedicated to demystifying complex server infrastructure issues and providing clear, actionable solutions for modern web stacks.
Sources
- https://nginx.org/en/docs/http/ngx_http_proxy_module.html
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502
- https://serverfault.com/questions/336209/nginx-connect-to-unix-var-run-php5-fpm-sock-failed-111-connection-refuse
- https://stackoverflow.com/questions/23756766/nginx-502-bad-gateway-php-fpm