← All articles

How to Fix 502 Bad Gateway in Nginx and PHP-FPM

Diagnose a 502 Bad Gateway between Nginx and PHP-FPM by checking logs, sockets, services, permissions, capacity, and timeouts in the right order.

Bakry Abdalsalam builds websites, applications, integrations, and WordPress products. Bakry Dev Hub documents the technical decisions behind this work.

THE SHORT VERSION

A 502 is a boundary failure: identify the failing upstream in the Nginx error log, then verify the PHP-FPM service, socket or port, permissions, and resource limits before changing timeouts.

A 502 Bad Gateway from Nginx means the proxy received an invalid response—or no usable response—from its upstream. In a PHP application, that upstream is usually PHP-FPM. The browser only shows the boundary error; the useful evidence is in the Nginx and PHP-FPM logs.

Do not begin by increasing every timeout. First determine whether Nginx cannot connect, PHP-FPM is crashing, or the application is taking too long.

Confirm which upstream failed

Reproduce the request once, record its time and path, and inspect the relevant logs immediately:

sudo tail -n 100 /var/log/nginx/error.log
sudo journalctl -u php8.3-fpm --since "10 minutes ago"
sudo systemctl status php8.3-fpm --no-pager

Replace php8.3-fpm with the installed service name. You can list likely units with:

systemctl list-units --type=service | grep -E 'php.*fpm'

Common Nginx messages point to different causes:

  • No such file or directory usually means the configured Unix socket path is wrong.
  • Permission denied means Nginx cannot access the socket or a parent directory.
  • Connection refused means nothing is listening on the configured port or socket.
  • upstream timed out means Nginx connected, but PHP-FPM did not send data within the configured interval.
  • upstream prematurely closed FastCGI stdout often accompanies a PHP crash, fatal error, process termination, or resource exhaustion.

If the response came from another proxy or CDN, confirm that the visible 502 is actually produced by your Nginx host before changing PHP-FPM.

Validate Nginx and PHP-FPM separately

Check the Nginx configuration before reloading it:

sudo nginx -t
sudo systemctl status nginx --no-pager

Then verify that PHP-FPM is active:

sudo systemctl is-active php8.3-fpm
sudo systemctl restart php8.3-fpm

A restart can restore service, but it is not a diagnosis. Read the journal after restarting so you know whether the process failed because of an invalid pool configuration, missing extension, memory pressure, or another repeatable condition.

If PHP-FPM will not start, validate its configuration using the binary available on your server:

sudo php-fpm8.3 -tt

Package names and binary paths vary by distribution. Use command -v php-fpm8.3 or inspect the service unit when unsure.

Match the FastCGI socket or port

The fastcgi_pass value in Nginx must match the listener configured by the PHP-FPM pool.

An Nginx Unix-socket configuration might contain:

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

Compare it with the pool configuration, commonly under /etc/php/8.3/fpm/pool.d/www.conf:

listen = /run/php/php8.3-fpm.sock

Inspect listeners without guessing:

sudo ss -ltnp | grep php
sudo find /run/php -maxdepth 1 -type s -ls

For TCP, both sides must use the same address and port, such as 127.0.0.1:9000. Avoid exposing PHP-FPM on a public interface.

After changing configuration:

sudo nginx -t && sudo systemctl reload nginx
sudo systemctl restart php8.3-fpm

Fix socket permissions deliberately

When the log says Permission denied, check the socket ownership and the user running Nginx:

ps -o user,group,cmd -C nginx
sudo stat /run/php/php8.3-fpm.sock

Configure the pool rather than applying a temporary chmod that disappears after restart:

listen.owner = www-data
listen.group = www-data
listen.mode = 0660

The correct user differs across distributions. Parent directories also need traversal permission. Do not solve the problem with mode 0777; that makes the boundary unnecessarily writable.

Check capacity, crashes, and slow application work

If connections work under light traffic but fail during load, inspect memory, disk, and PHP-FPM pool saturation:

free -h
df -h
sudo dmesg -T | grep -i -E 'out of memory|killed process'
sudo journalctl -u php8.3-fpm -p warning --since today

Review pm.max_children, pm.max_requests, and the pool process manager against measured memory use. Raising pm.max_children without available RAM can make the kernel kill PHP workers and create more 502 responses.

For slow requests, enable the PHP-FPM slow log temporarily and profile the application. Database locks, external API calls, filesystem latency, and synchronous report generation are usually better fixed at their source. Long work should often move to a queue; see the Laravel queue and Supervisor troubleshooting guide.

Nginx documents fastcgi_read_timeout as the interval between successive reads from FastCGI, not a total request-duration limit. Increase it only when a legitimate endpoint is expected to remain silent for longer, and keep the scope narrow.

Verify the fix

Test both a simple PHP route and the path that originally failed:

curl -sS -o /dev/null -w '%{http_code} %{time_total}\n' https://example.com/health
curl -sS -o /dev/null -w '%{http_code} %{time_total}\n' https://example.com/original-path

Then confirm:

  1. nginx -t succeeds.
  2. Nginx and PHP-FPM remain active after several requests.
  3. The error log does not receive a new upstream failure.
  4. A deployment restart does not recreate the socket mismatch.
  5. Monitoring sees both the public endpoint and the PHP-FPM service.

Common mistakes and prevention

Avoid these shortcuts:

  • Raising all timeouts before reading the error log.
  • Pointing Nginx at a socket from an older PHP version.
  • Starting php artisan serve as a production upstream.
  • Changing socket permissions manually instead of fixing the pool.
  • Restarting services repeatedly without checking why they stopped.
  • Hiding a capacity problem by creating more PHP-FPM children than memory allows.

Keep the Nginx and PHP-FPM configuration in version control, validate both during deployment, and monitor service state, error rate, memory pressure, and upstream response time. The broader Nginx reverse proxy production checklist covers the surrounding boundary.

Frequently asked questions

Is the problem fixed if restarting PHP-FPM removes the 502?

Service is restored, but the cause is not necessarily fixed. Check the preceding journal entries, memory pressure, pool limits, and deployment events so the same failure does not return.

Should Nginx connect to PHP-FPM through a Unix socket or TCP?

Both can work. A Unix socket is common when both services share a host; TCP is often clearer across containers. The important requirements are a private listener, matching configuration, controlled permissions, and monitoring.

Official references

Have a question about this guide or an idea for a technical collaboration? Contact Bakry through the Dev Hub.

End of field note.