Linux  ·  critical  ·  Web, PHP & databases

nginx 502 Bad Gateway

nginx reached the upstream and got nothing usable back. The nginx error log names the reason precisely and is the only place worth starting.

What you see

A 502 page for every request, or intermittently under load. nginx itself is running fine.

What is actually wrong

The application behind it is not running, is listening somewhere else, is refusing the socket's permissions, or is taking longer than the configured timeout.

Codes and articles

502 Bad Gatewayconnect() failedupstream prematurely closedno live upstreams111: Connection refused

Fixes (3)

Confirm the upstream is running and listening where nginx expects
Root shell20 minuteslow riskreversible

The log says connection refused, or names a socket that does not exist.

  1. Read the actual error. Every 502 has a specific cause written here.

    Shell
    sudo tail -50 /var/log/nginx/error.log
  2. Check the upstream is running.

    Shell
    systemctl status php8.2-fpm --no-pager -lsudo ss -tlnp | grep -E '9000|3000|8080'ls -l /run/php/

    The mismatch between what nginx is configured to reach and what the application actually listens on accounts for most of these — commonly a fastcgi_pass pointing at a socket path that changed with a PHP version upgrade.

  3. Compare the two configurations directly.

    Shell
    grep -r 'fastcgi_pass\|proxy_pass' /etc/nginx/sites-enabled/grep -E '^listen' /etc/php/*/fpm/pool.d/www.conf
  4. Correct whichever is wrong and reload rather than restart, so live connections are not dropped.

    Shell
    sudo nginx -t && sudo systemctl reload nginx
  5. If the upstream keeps dying, read its own log rather than nginx's.

    Shell
    sudo journalctl -u php8.2-fpm -n 50 --no-pager
Confirm it workedA request returns the application's response and the error log is quiet.
Shell
curl -sI http://localhost/ | head -3
If you need to undo itKeep a copy of the site configuration before editing it.
Fix the socket permissions
Root shell20 minutesmedium riskreversible

The log says permission denied on a socket path.

  1. Look at the socket and who owns it.

    Shell
    ls -l /run/php/php8.2-fpm.sockid www-dataps -o user= -C nginx | sort -u
  2. Set the pool's socket ownership to the user nginx runs as.

    Shell
    sudo sed -i 's/^;*listen.owner.*/listen.owner = www-data/; s/^;*listen.group.*/listen.group = www-data/; s/^;*listen.mode.*/listen.mode = 0660/' /etc/php/8.2/fpm/pool.d/www.confsudo systemctl restart php8.2-fpm

    The socket is created by php-fpm with the ownership in its pool configuration. Changing the permissions by hand works until the next restart recreates it, which is why this fault appears to come back on its own.

  3. On a system with SELinux, check for a denial before assuming it is Unix permissions.

    Shell
    sudo ausearch -m avc -ts recent | grep -i nginx | tail -20sudo setsebool -P httpd_can_network_connect 1
  4. Check the directory permissions too — nginx needs execute on every directory in the socket's path.

Confirm it workedThe socket is readable by the nginx user and requests succeed.
Shell
sudo -u www-data test -w /run/php/php8.2-fpm.sock && echo writable
If you need to undo itRestore the previous pool configuration from a backup copy.
Deal with an upstream that is too slow or crashing
Root shell40 minutesmedium riskreversible

The log says upstream timed out, or prematurely closed the connection.

  1. Find out whether it is slow or dying. A premature close means the worker exited mid-request.

    Shell
    sudo journalctl -u php8.2-fpm --since '1 hour ago' | grep -iE 'exited|signal|oom|segfault'dmesg -T | grep -i 'killed process'

    A worker killed by the OOM killer produces exactly the same 502 as a slow query, and raising the timeout for it makes matters worse. One command separates them.

  2. If it is being killed for memory, look at the pool's memory limit and worker count rather than the timeout.

    Shell
    grep -E '^pm|memory_limit' /etc/php/8.2/fpm/pool.d/www.conf /etc/php/8.2/fpm/php.ini
  3. If it is genuinely slow, raise the timeouts on both sides — they must match or the shorter one wins.

    Shell
    grep -rn 'fastcgi_read_timeout\|proxy_read_timeout' /etc/nginx/grep -n 'request_terminate_timeout\|max_execution_time' /etc/php/8.2/fpm/pool.d/www.conf /etc/php/8.2/fpm/php.ini
  4. Set them consistently, and treat a long-running request as something to fix rather than to accommodate.

    Shell
    sudo sed -i 's/^;*request_terminate_timeout.*/request_terminate_timeout = 120/' /etc/php/8.2/fpm/pool.d/www.confsudo systemctl restart php8.2-fpm
  5. Enable the slow log so the actual slow function is recorded.

    Shell
    sudo sed -i 's|^;*slowlog.*|slowlog = /var/log/php-fpm-slow.log|; s/^;*request_slowlog_timeout.*/request_slowlog_timeout = 5s/' /etc/php/8.2/fpm/pool.d/www.confsudo systemctl restart php8.2-fpm
Confirm it workedRequests complete and the slow log names anything still over the threshold.
Shell
sudo tail -20 /var/log/php-fpm-slow.log
If you need to undo itRestore the previous timeout values from the backup copies of the configuration.

Where this stops. This write-up was written and checked by hand. It says what each step changes, how to confirm it worked and how to reverse it, and anything destructive is flagged before you reach it. If it does not match what your machine is doing, search the Support Centre for the exact code or message — and when something needs a person, get in touch.