Skip to content
Innovialabs

Nginx 502 Bad Gateway and 504 Timeout: Causes and Fixes

Practice5 min

In short

A 502 means Nginx got no valid response from the backend (it is down, not listening or the socket is inaccessible), while a 504 means the backend was too slow; error.log almost always names the cause.

How 502 and 504 differ

As a reverse proxy, Nginx does not build the page itself. It forwards the request to an upstream application (PHP-FPM, Node.js, Python, Go) and relays the response.

  • 502 Bad Gateway: Nginx could not get a proper response. The connection was refused, the socket is missing, the process died mid-response, or the response headers did not fit into the buffer.
  • 504 Gateway Timeout: the connection worked, but the backend did not answer in time.

In short: 502 is "the backend is gone or answered garbage", 504 is "the backend is there but too slow".

Step 1. Read error.log

Do not tweak the config blindly. Open the error log first:

sudo tail -n 50 /var/log/nginx/error.log

The path may differ, so check the error_log directive. Typical lines and their meaning:

Log messageCause
connect() failed (111: Connection refused)app not running or listening on another port
connect() to unix:... failed (2: No such file or directory)wrong socket path or PHP-FPM is down
connect() to unix:... failed (13: Permission denied)the Nginx user cannot access the socket
upstream prematurely closed connectionthe process crashed or was killed mid-response
upstream sent too big headerresponse headers do not fit into the buffer
upstream timed out (110: Connection timed out)backend is slower than the timeout, a 504

Step 2. Check the upstream process and socket

Make sure the app is alive and listening exactly where proxy_pass or fastcgi_pass points:

systemctl status php8.2-fpm        # or your service name
ss -ltnp | grep 3000               # Node.js TCP port
ls -l /run/php/                    # PHP-FPM unix sockets
curl -I http://127.0.0.1:3000      # request bypassing Nginx

If a direct curl fails, Nginx is not the problem. Check the app's own logs with journalctl -u service-name.

A common mistake is an address mismatch: Nginx calls 127.0.0.1:3000 while the app listens on IPv6 localhost only, or on a different port. Another one: after a PHP upgrade the socket name changed, but the config still points to the old one.

Step 3. Socket permissions

On Permission denied, compare the Nginx user (user in nginx.conf) with the socket owner. In the PHP-FPM pool config set:

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

Restart PHP-FPM. If SELinux is enabled, the denial may come from it, so check the audit log.

Step 4. Timeouts (fixing 504)

First find out why the request is slow: a heavy SQL query, an external API, a big report. Raise timeouts only for genuinely long operations, and preferably per location:

location /reports/ {
    proxy_connect_timeout 10s;
    proxy_read_timeout    120s;
    proxy_send_timeout    120s;
    proxy_pass http://127.0.0.1:3000;
}

For PHP-FPM the equivalent is fastcgi_read_timeout. Remember that PHP has its own max_execution_time and the pool has request_terminate_timeout. If these are shorter than the Nginx timeout, the process is killed first and you get a 502 instead of a 504.

Long jobs such as imports or file generation belong in a background queue, with the user receiving a status instead of waiting.

Step 5. Buffers

upstream sent too big header usually comes from large cookies or many headers. Increase the buffers:

proxy_buffer_size        16k;
proxy_buffers            8 16k;
proxy_busy_buffers_size  32k;
# for PHP-FPM
fastcgi_buffer_size      32k;
fastcgi_buffers          16 16k;

Also check whether the app inflates cookies for no reason.

Step 6. PHP-FPM and Node.js crashes

If 502s appear under load and then disappear, the backend cannot keep up:

  • PHP-FPM: look for server reached pm.max_children setting in the pool log. All workers are busy. Raise pm.max_children with memory in mind, since each worker uses its own.
  • Node.js: an unhandled exception kills the whole process. Run the app under a process manager (systemd, PM2, Docker with restart) so it comes back automatically, and find the cause in the logs.
  • OOM killer: run dmesg | grep -i kill. If the kernel kills the process for lack of memory, you need optimization or more RAM.

After any change, test the config and reload without stopping the server:

sudo nginx -t && sudo systemctl reload nginx

Checklist

  1. Read error.log and find the exact error.
  2. Confirm the upstream is running and answers directly.
  3. Match the address, port or socket path.
  4. Check unix socket permissions.
  5. For 504, find the slow part, then raise the timeout locally.
  6. For "too big header", increase buffers.
  7. Check worker limits and the OOM killer.

FAQ

Usually the backend cannot handle peak load: PHP-FPM runs out of workers, the Node.js process crashes and restarts, or the OOM killer takes it down. Match the error times in error.log against the app logs and dmesg.

We can help with this

If you'd rather get the result than figure it all out yourself — this is what we do every day.

Discuss a project

Read next

Have an idea or a task? Tell us about it

We'll reply within one business day and propose a solution. The consultation is free.