Website down: fix 502, 503 and 504 errors fast

Updated November 2026

A 502, 503 or 504 page means the web server is up but cannot get an answer from the application behind it. The error log almost always says why. Read it before you restart anything, then fix the cause: a stopped service, a wrong socket, a full pool, a slow database, a full disk or too little memory.

Do this first.

  1. Read the last 50 lines of the web server error log: sudo tail -n 50 /var/log/nginx/error.log.
  2. Check that nginx and PHP-FPM (or your app) are running: systemctl status nginx 'php*-fpm*'.
  3. Check disk and memory: df -h and free -h.
  4. If a restart is needed, test the configuration first with sudo nginx -t.

What each code means

Gateway errors from nginx and their usual causes
CodeMeaningUsual cause
502 Bad GatewayThe upstream refused, closed or does not existPHP-FPM or app stopped, wrong socket path, socket permissions, app crashed, all upstream servers marked down
503 Service UnavailableNginx itself refused the requestRate or connection limits (limit_req, limit_conn), or an application in maintenance mode passing on its own 503
504 Gateway TimeoutThe upstream accepted but did not answer in timeSlow PHP or database query, full PHP-FPM pool, timeout too short for a known long task
429 Too Many RequestsA rate limit, if configured to use 429The same limits as 503, with limit_req_status 429
Read error.log 502: app or socket 503: limits 504: slow upstream Is it running? Right path? limit_req, limit_conn Pool full? Slow DB?
Start with the log line, then follow the branch for the status code.

Step 1: are the services running?

systemctl status nginx
# Debian/Ubuntu: one service per PHP version (php8.3-fpm, php8.4-fpm, ...)
systemctl list-units 'php*-fpm*'
# RHEL, Rocky, AlmaLinux, Fedora
systemctl status php-fpm

# Why did it stop? Last log lines of the unit
sudo journalctl -u php8.4-fpm --since "-1 hour"   # use your unit name

If a service is stopped, test its configuration before you start it, so a typo does not take down the rest:

sudo nginx -t
sudo php-fpm8.4 -t        # Debian/Ubuntu (match your version)
sudo php-fpm -t           # RHEL family
sudo systemctl restart php8.4-fpm   # restarts PHP-FPM: running requests are cut off
sudo systemctl reload nginx         # graceful, no dropped connections

Step 2: read the error log

sudo tail -n 100 /var/log/nginx/error.log
sudo grep -E 'upstream|connect\(\)' /var/log/nginx/error.log | tail -n 20

Match the message to its fix:

connect() to unix:/run/php/... failed (2: No such file or directory)
The socket in fastcgi_pass does not exist. PHP-FPM is stopped, or nginx points at an old PHP version’s socket after an upgrade. Compare ls /run/php/ (Debian/Ubuntu) or ls /run/php-fpm/ (RHEL) with sudo grep -rn fastcgi_pass /etc/nginx/, and with the listen = line in the pool file (/etc/php/8.x/fpm/pool.d/www.conf or /etc/php-fpm.d/www.conf).
(13: Permission denied) while connecting to upstream
The nginx user cannot open the socket. Set listen.owner and listen.group in the pool to the nginx user (www-data on Debian/Ubuntu, nginx on RHEL) with listen.mode = 0660, then restart PHP-FPM. On RHEL with SELinux, a proxy to a TCP port can be denied by policy; check sudo ausearch -m avc -ts recent. If the denial is for name_connect, the usual fix is sudo setsebool -P httpd_can_network_connect 1. Do not disable SELinux.
(111: Connection refused) while connecting to upstream
Nothing listens on that address and port. The app (Node, Python, Java, Docker container) is down or listens elsewhere. Check with sudo ss -ltnp.
upstream timed out (110: Connection timed out) while reading response header
The 504 case. The upstream took longer than fastcgi_read_timeout or proxy_read_timeout (both default to 60 seconds) without sending anything. Find the slow part (step 3) rather than raising the timeout first.
limiting requests, excess: ... by zone
The 503 (or 429) case. A limit_req rule rejected the request. See step 5.

Step 3: full pool or slow backend

Each PHP-FPM child handles one request at a time, and pm.max_children caps how many run at once. When all are busy, new requests wait and then time out. PHP-FPM logs a warning when it hits the cap:

# Debian/Ubuntu log; on RHEL use /var/log/php-fpm/error.log
sudo grep -i 'max_children' /var/log/php8.4-fpm.log | tail
# How many PHP-FPM processes, and how much memory each uses (RSS in KB)
ps -C php-fpm8.4 -o pid,rss,etime,args --sort=-rss | head   # RHEL: ps -C php-fpm ...

Do not just raise pm.max_children. Multiply it by the memory per child: 20 children at 80 MB each need 1,600 MB. If that does not fit in free RAM, the server will swap or the kernel will kill processes. Find out why requests are slow instead. Set request_slowlog_timeout = 5s and a slowlog path in the pool to get a backtrace of every request slower than five seconds. Check the database for long-running queries (SHOW FULL PROCESSLIST; in MySQL/MariaDB, pg_stat_activity in PostgreSQL). A sudden flood of requests is covered in High load or out of memory and DDoS and floods.

Raise timeouts only for a known slow task, such as an export, and only in that location:

location = /admin/export.php {
    fastcgi_read_timeout 300s;
    include fastcgi_params;   # plus your usual fastcgi settings
    fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}

Step 4: disk and memory

df -h          # a full disk stops logs, sessions, caches and databases
df -i          # out of inodes looks like a full disk
free -h
sudo journalctl -k --since "-6 hours" | grep -i -E 'out of memory|killed process'

If the kernel’s out-of-memory killer ended PHP-FPM, MySQL or your app, that explains a sudden 502. Reduce pm.max_children, find the memory-hungry process, or add RAM. For a full disk, see Disk full. If the database itself will not start, see Database won't start.

Step 5: rate limits returning 503 or 429

The nginx limit_req and limit_conn modules return 503 by default when a client goes over the limit. A limit that is too strict blocks real users, especially behind a proxy or CDN where all visitors seem to come from a few IP addresses.

sudo grep -rn -E 'limit_req|limit_conn' /etc/nginx/

A 503 can also come from a maintenance-mode file left behind by an application update. Check the application’s own docs.

Confirm it is fixed

curl -sS -o /dev/null -w '%{http_code} %{time_total}s\n' https://example.com/
sudo tail -f /var/log/nginx/error.log      # should stay quiet

Prevent it next time

When to get help

Call your host if the server itself is unreachable, if the network or load balancer is theirs, or if a hardware fault appears in the kernel log. If you find unknown processes or files while you investigate, stop and follow Signs of compromise.

Sources