Nothing derails a production deployment faster than a sudden 502 Bad Gateway error staring back at you from the browser. I have spent more hours than I care to admit tracking down these failures across dozens of NGINX servers, and the root cause is almost always hiding in plain sight inside your error logs. Fixing NGINX FastCGI 502 errors comes down to understanding how NGINX talks to your backend, reading the right log files, and applying the correct fix for the specific failure mode.
In this guide, I will walk you through the complete process of diagnosing and resolving 502 errors caused by FastCGI backends like PHP-FPM and Gunicorn. Whether you are running a single WordPress site or a containerized microservice architecture, the same principles apply. By the end, you will have a repeatable troubleshooting workflow and copy-paste commands you can use the next time a 502 strikes.
Most 502 errors fall into one of seven categories: the backend service is not running, the socket path is wrong, permissions are blocking access, the connection is refused, workers are exhausted, a timeout was hit, or a buffer is too small. We will cover all of them.
How NGINX and FastCGI Work Together?
Before fixing anything, it helps to understand the communication chain. NGINX sits in front as a reverse proxy. When a request comes in for a dynamic file, like a PHP script or a Python endpoint, NGINX does not execute it directly. Instead, it forwards the request to a FastCGI backend process using the fastcgi_pass directive.
The backend can be PHP-FPM, Gunicorn, or any server that speaks the FastCGI protocol. NGINX connects to this backend through either a Unix domain socket (a file on disk like /run/php/php8.2-fpm.sock) or a TCP socket (an IP address and port like 127.0.0.1:9000). If NGINX cannot reach the backend for any reason, it returns a 502 Bad Gateway error to the client.
Think of it like a phone call. NGINX dials the backend, and if nobody picks up, or the line is dead, NGINX tells the caller “bad gateway.” The error is NGINX saying it got an invalid or no response from the upstream server it was trying to reach.
This distinction matters for fixing NGINX FastCGI 502 errors because the fix always depends on why NGINX failed to get a valid response. A service that is not running has a completely different fix than a socket permission issue, even though both produce the same 502 in the browser.
7 Root Causes of NGINX FastCGI 502 Errors
Over years of managing web servers, I have seen the same set of root causes appear repeatedly. Here are the seven most common reasons NGINX returns a 502 when working with FastCGI backends, along with the specific error log entries that identify each one.
1. PHP-FPM or Backend Service Not Running
This is the most common cause and the easiest to fix. If PHP-FPM (or your Gunicorn process) is not running at all, NGINX has nothing to connect to. The error log will show something like connect() to unix:/run/php/php-fpm.sock failed (2: No such file or directory) or connect() failed (111: Connection refused) when using TCP.
I have seen this happen after server reboots where the PHP-FPM service was not enabled for automatic start, after PHP version upgrades that changed the service name, and after out-of-memory kills that silently terminated the backend process. On Reddit, one user shared that updating PHP caused NGINX to start throwing 502 errors because the new PHP version installed a service under a different name.
2. Socket Path Mismatch
NGINX and your backend must agree on exactly where the socket lives. If your NGINX config points to /var/run/php-fpm.sock but PHP-FPM is listening on /run/php/php8.2-fpm.sock, you get a 502. The error log shows No such file or directory even though PHP-FPM is running fine.
This mismatch happens frequently after PHP upgrades. PHP 8.1 might use /run/php/php8.1-fpm.sock while PHP 8.2 uses /run/php/php8.2-fpm.sock. If you update PHP but forget to update the fastcgi_pass directive in your NGINX configuration, the 502 appears immediately.
3. Socket Permission Denied
Even when the socket file exists, NGINX might lack permission to access it. The error log will show connect() to unix:... failed (13: Permission denied). This usually happens when PHP-FPM and NGINX run as different users, or when the socket file has restrictive permissions.
On some distributions, PHP-FPM creates its socket with permissions limited to the www-data group, but NGINX might be running as nginx or http. The fix involves adjusting the listen.mode and listen.group settings in your PHP-FPM pool configuration.
4. Connection Refused on TCP Port
If you are using TCP sockets instead of Unix sockets, a Connection refused error in the log means nothing is listening on that IP and port. The backend might be configured to listen on a different port, or it might be binding to a different network interface.
For example, if PHP-FPM is set to listen on 127.0.0.1:9000 but your NGINX config says fastcgi_pass 127.0.0.1:9001;, the connection fails. In Docker environments, this gets trickier because 127.0.0.1 inside the NGINX container is not the same as 127.0.0.1 inside the PHP-FPM container. You need to use the container hostname or Docker network IP instead.
5. PHP-FPM Worker Exhaustion
PHP-FPM manages a pool of worker processes. When all workers are busy and the queue is full, new requests get no response. The error log might show no live upstreams while connecting to upstream or you might see intermittent 502 errors that only appear under heavy load.
This is one of the sneakier causes because it only shows up when traffic spikes. A user on Reddit reported intermittent 502 errors under load in a Docker Swarm setup, and the culprit turned out to be hitting the configured pm.max_children limit. The default pool configuration is often too conservative for production traffic.
Check your PHP-FPM pool configuration for these key settings: pm.max_children, pm.max_requests, and pm.process_idle_timeout. If your application has slow database queries or external API calls, workers get tied up and the pool exhausts quickly.
6. FastCGI Timeout Exceeded
If your PHP script or Python application takes too long to respond, NGINX gives up and returns a 502. The relevant error log entry is upstream timed out (110: Connection timed out) or sometimes a 504 Gateway Timeout depending on which timeout directive is triggered.
NGINX has three timeout directives that affect FastCGI connections. fastcgi_connect_timeout controls how long NGINX waits to establish a connection. fastcgi_send_timeout controls how long it waits between successive write operations. fastcgi_read_timeout controls how long it waits for a response from the backend. On the PHP-FPM side, request_terminate_timeout kills scripts that run too long.
7. Buffer Size Too Small
Less common but well-documented: if your PHP script generates a very large response header or output, NGINX’s FastCGI buffers might not be big enough to hold it. A StackOverflow user discovered that increasing the buffer size resolved their 502 error, asking “why?” afterward. The answer is that when the buffer overflows, NGINX cannot process the upstream response correctly.
The relevant directives are fastcgi_buffer_size for the response header and fastcgi_buffers for the response body. Default values work for most sites, but applications with very large cookies or complex headers can exceed them.
Step-by-Step Diagnosis for Fixing NGINX FastCGI 502 Errors
To fix NGINX FastCGI 502 errors efficiently, follow this diagnosis workflow in order. Each step builds on the previous one and narrows down the root cause.
Step 1: Check if the backend service is running. Run the appropriate command for your system:
systemctl status php8.2-fpm
or for Python/Gunicorn:
systemctl status gunicorn
If the service shows as inactive or failed, that is your answer. Start it with systemctl start php8.2-fpm and enable it for boot with systemctl enable php8.2-fpm.
Step 2: Read the NGINX error log. This is your single most valuable diagnostic tool. Almost every user on ServerFault and Reddit starts here. Run:
tail -n 50 /var/log/nginx/error.log
Look for lines containing connect() failed, upstream timed out, or no live upstreams. The specific error code and message tell you exactly which of the seven root causes you are dealing with. Error code 2 means the socket file does not exist. Error code 13 means permission denied. Error code 111 means connection refused. Error code 110 means a timeout occurred.
Step 3: Verify the socket configuration matches. Compare the fastcgi_pass value in your NGINX site configuration with the listen directive in your PHP-FPM pool configuration. They must be identical.
Check NGINX:
grep fastcgi_pass /etc/nginx/sites-enabled/*
Check PHP-FPM:
grep listen /etc/php/8.2/fpm/pool.d/www.conf
If NGINX says fastcgi_pass unix:/run/php/php-fpm.sock; but PHP-FPM says listen = /run/php/php8.2-fpm.sock, you have found your mismatch.
Step 4: Check PHP-FPM pool status and worker usage. Enable the PHP-FPM status page by adding pm.status_path = /status to your pool configuration, then configure NGINX to pass requests to that path. Visit it to see how many workers are active versus idle. If all workers are consistently active, you are hitting the pm.max_children limit.
Alternatively, check process count directly:
ps aux | grep php-fpm | wc -l
Compare the running count to your configured pm.max_children value.
Step 5: Test the backend independently. Bypass NGINX entirely and test whether the backend responds on its own. For a Unix socket, use:
SCRIPT_NAME=/status SCRIPT_FILENAME=/status REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/php8.2-fpm.sock
For a TCP socket, use:
curl http://127.0.0.1:9000/status
If the backend does not respond on its own, the problem is with the backend itself, not NGINX. If it responds fine, the issue is in the NGINX-to-backend communication layer.
How to Fix Each 502 Cause?
Now that you have identified the root cause through diagnosis, here are the specific fixes for each scenario.
Fix 1: Start and Enable the Backend Service
If PHP-FPM or Gunicorn is not running, start it and make sure it starts on boot:
sudo systemctl start php8.2-fpm
sudo systemctl enable php8.2-fpm
Check if it crashed due to a configuration error:
journalctl -u php8.2-fpm --no-pager -n 50
If the service fails to start, the journal output will tell you why. Common issues include syntax errors in the pool configuration file or missing PHP extensions that were removed during an upgrade.
Fix 2: Correct the Socket Path
Make the fastcgi_pass directive in NGINX match the listen directive in PHP-FPM exactly. Open your NGINX site configuration:
sudo nano /etc/nginx/sites-available/example.com
Update the fastcgi_pass line to use the correct socket path:
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
Then test and reload NGINX:
sudo nginx -t && sudo systemctl reload nginx
Fix 3: Fix Socket Permissions
If the error log shows permission denied, adjust the socket permissions in your PHP-FPM pool configuration:
sudo nano /etc/php/8.2/fpm/pool.d/www.conf
Set or update these lines:
listen.mode = 0660
listen.group = nginx
listen.owner = www-data
Make sure the group matches the user NGINX runs as. On Ubuntu this is typically www-data, on CentOS it is nginx. Restart PHP-FPM after making changes:
sudo systemctl restart php8.2-fpm
Fix 4: Increase PHP-FPM Worker Count
If workers are exhausting under load, increase pm.max_children in your pool configuration:
sudo nano /etc/php/8.2/fpm/pool.d/www.conf
For dynamic process management, adjust these values:
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
The right value for pm.max_children depends on your server’s RAM and how much memory each PHP process consumes. A rough formula: max_children = Total RAM / Memory per process. Check average process memory with ps -ylC php-fpm --sort:rss and look at the RSS column.
Also consider setting pm.max_requests = 500 to recycle workers periodically, which prevents memory leaks from accumulating over time.
Fix 5: Adjust Timeout Settings
If long-running scripts cause timeouts, increase the timeout values. In your NGINX configuration, inside the location ~ \.php$ block:
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 120s;
In your PHP-FPM pool configuration:
request_terminate_timeout = 120
Keep in mind that increasing timeouts is a bandage. If your scripts genuinely need that much time, consider moving long tasks to background jobs using a queue system instead. But for legitimate slow operations like large report generation or bulk imports, the timeout increase is necessary.
Fix 6: Increase Buffer Sizes
If the error log points to buffer issues or your application sends very large headers, add these directives to your NGINX FastCGI location block:
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
The default fastcgi_buffer_size is usually 4k or 8k depending on your platform. Applications with large session cookies, elaborate authentication headers, or custom response headers from PHP can easily exceed these defaults.
PHP-FPM vs Gunicorn: 502 Error Differences
Most guides cover only PHP-FPM, but if you run Python applications behind NGINX with Gunicorn, the 502 troubleshooting process is slightly different. The NGINX side is identical since it uses the same proxy_pass or fastcgi_pass mechanism. The difference is on the backend.
Gunicorn uses worker processes similar to PHP-FPM, but you configure them with command-line flags or a config file rather than a pool configuration. Check Gunicorn status with systemctl status gunicorn and its logs with journalctl -u gunicorn. Common Python-specific 502 causes include Django application errors that crash the worker, gevent or eventlet worker timeouts, and binding to the wrong host-port combination.
In a Docker Compose setup, the networking adds another layer. Gunicorn might listen on 0.0.0.0:8000 inside its container, but NGINX needs to connect using the Docker service name like fastcgi_pass web:8000; rather than 127.0.0.1. This networking mismatch is a frequent source of 502 errors in containerized deployments.
Preventing 502 Errors Before They Happen
Fixing 502 errors reactively gets old fast. Here are the preventive measures I put in place on every server I manage.
Enable automatic service restarts. Add PHP-FPM and Gunicorn to systemd’s restart logic. In your service override file:
sudo systemctl edit php8.2-fpm
Add:
[Service]
Restart=always
RestartSec=5
Set up monitoring. Use a tool like Telegraf, Prometheus, or a hosted monitoring service to track PHP-FPM pool metrics. Watch the active process count relative to your pm.max_children limit. When active processes start approaching the limit consistently, you know you need to scale before 502 errors appear.
Configure log-based alerts. Set up alerts that trigger when the NGINX error log sees a spike in 502 responses. Tools like Datadog, Grafana with Loki, or even a simple script watching the error log can catch problems before users report them.
Plan resources for traffic spikes. If you know your application gets traffic surges during certain events or times of day, pre-scale your worker pools. A PHP-FPM pool with too few workers will 502 under load even if everything else is configured correctly.
Use health checks in Docker and Kubernetes. In containerized environments, configure health check endpoints so the orchestrator can restart unhealthy pods automatically. A Gunicorn worker that has stopped processing requests should be detected and recycled before NGINX starts returning 502 errors to real users.
Test configuration changes before deploying. Always run sudo nginx -t before reloading NGINX, and sudo php-fpm8.2 -t before restarting PHP-FPM. A typo in either configuration file can take your entire site offline with a 502 error.
How do I fix error 502 on nginx?
To fix a 502 error on NGINX, start by checking if your backend service (PHP-FPM, Gunicorn) is running with systemctl status. If it is not running, start it with systemctl start. If it is running, check the NGINX error log at /var/log/nginx/error.log for the specific cause. The most common fixes are restarting the backend service, correcting the socket path in fastcgi_pass, fixing socket permissions, increasing worker count, or adjusting timeout settings.
What causes a 502 Bad Gateway error with nginx and PHP-FPM?
A 502 Bad Gateway error with NGINX and PHP-FPM occurs when NGINX cannot get a valid response from the PHP-FPM backend. The most common causes are: PHP-FPM service not running, socket path mismatch between NGINX and PHP-FPM configuration, socket permission denied, PHP-FPM worker pool exhaustion under load, request timeout exceeding configured limits, or FastCGI buffer sizes being too small for the response.
How do I check if PHP-FPM is running?
Run systemctl status php-fpm or systemctl status php8.2-fpm (adjust the version number). You can also check for running processes with ps aux | grep php-fpm. If PHP-FPM is running, you will see a master process and several worker processes. If nothing appears, the service is not running and needs to be started with sudo systemctl start php8.2-fpm.
Can a firewall cause NGINX 502 Bad Gateway?
Yes, a firewall can cause 502 errors if it blocks the connection between NGINX and the backend service. This happens most often when using TCP sockets. If NGINX connects to PHP-FPM via a TCP port and the firewall blocks that port, NGINX receives a connection refused or timeout error and returns 502. Check iptables rules or firewalld configuration to ensure the backend port is allowed on the loopback interface or between containers.
Does clearing cache fix 502 errors?
Clearing browser cache or server-side cache does not fix 502 errors caused by backend failures. A 502 Bad Gateway means NGINX cannot reach or get a valid response from the upstream server. The fix must address the underlying cause: start the backend service, fix socket configuration, increase workers, or adjust timeouts. However, if a cached error page is the issue, clearing the NGINX proxy cache might help after the actual backend problem is resolved.
Is a 502 error a security risk?
A 502 error itself is not a security vulnerability, but it can indicate underlying issues that may have security implications. If 502 errors are caused by resource exhaustion, your server could be under a denial-of-service attack. If errors started after a configuration change, the change may have introduced a weakness. Additionally, default 502 error pages can reveal server software versions, so customizing error pages in NGINX is a good practice to reduce information disclosure.
Conclusion
Fixing NGINX FastCGI 502 errors is a systematic process: check the backend service, read the error log, verify the socket configuration, inspect worker utilization, and test the backend independently. The error log is always your starting point, and the specific error code tells you exactly which of the seven root causes you are dealing with.
I recommend keeping a checklist handy for the next time a 502 appears. Run systemctl status, check /var/log/nginx/error.log, compare socket paths, and work through the fixes in order. Most 502 errors resolve in under ten minutes once you know the pattern. For long-term stability, invest in monitoring, enable automatic restarts, and size your worker pools for peak traffic rather than average load.
