Guide · Hosting & CDN

nginx 502 and 504 Errors: Timeout, Buffer and Keepalive Tuning

Tune nginx for 502 and 504 errors with the right directives: proxy_read_timeout, fastcgi_read_timeout, buffer sizes, upstream keepalive, and how to read the error log.

By SiteDownCheck Editorial · Updated October 5, 2026 · 8 min read

When nginx returns 502 or 504, it is telling you that the application behind it didn't answer correctly or in time. Raising timeouts blindly makes some 504s go away and hides the actual problem. Tuning works best when you read the error log first, pick the directive that matches the log line, and change one thing at a time. This guide maps log messages to the correct nginx directives and shows configuration that works for reverse-proxied apps (Node, Gunicorn, Puma) and PHP-FPM.

For the overall diagnosis of 502 across layers, see fix 502 bad gateway and fix 504 gateway timeout. This page is the nginx-specific tuning companion.

502 vs 504 in nginx terms

  • 502 Bad Gateway: nginx could not get a valid response. The upstream refused the connection, reset it, returned malformed data, or the socket was missing.
  • 504 Gateway Timeout: nginx waited and gave up. The upstream accepted the connection but did not respond within the time limit.

The error log distinguishes them. Find it:

sudo tail -n 100 /var/log/nginx/error.log
sudo nginx -T 2>/dev/null | grep -E "error_log|access_log"   # where logs are configured

Match the log line to the fix

Error log messageMeaningWhere to look
connect() failed (111: Connection refused) while connecting to upstreamNothing listening at that addressApp crashed or not started, wrong port
connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory)Socket path wrong or service downPHP-FPM status, fastcgi_pass path
connect() to unix:... failed (13: Permission denied)nginx user cannot access socketSocket owner and mode
upstream timed out (110: Connection timed out) while reading response header from upstreamApp too slowproxy_read_timeout or fastcgi_read_timeout, and app performance
upstream timed out (110: Connection timed out) while connecting to upstreamCannot establish connection in timeproxy_connect_timeout, backlog, firewall
recv() failed (104: Connection reset by peer) while reading response header from upstreamUpstream closed the connection abruptlyApp crash, worker killed, keepalive mismatch
upstream prematurely closed connection while reading response header from upstreamUpstream died mid-responseApp logs, memory kills, request timeouts at app
upstream sent too big header while reading response header from upstreamResponse headers exceed bufferproxy_buffer_size or fastcgi_buffer_size
no live upstreams while connecting to upstreamAll upstream servers marked failedmax_fails, fail_timeout, backend health

Check which one you have before changing anything. The Port Checker can test that the upstream port is open if it is reachable from outside, and ss -ltnp tells you locally.

sudo ss -ltnp | grep -E ':(3000|8000|8080|9000)\b'
ls -l /run/php/
systemctl status php8.2-fpm --no-pager

Timeouts for proxied applications

For proxy_pass upstreams, three timeouts matter. All three default to 60 seconds in nginx.

location / {
    proxy_pass http://127.0.0.1:3000;

    proxy_connect_timeout 5s;    # time to establish the TCP connection
    proxy_send_timeout    60s;   # max gap between two writes of the request to upstream
    proxy_read_timeout    60s;   # max gap between two reads of the response from upstream
}

Key facts that people get wrong:

  • proxy_read_timeout is the time between two successive reads, not the total response time. A streaming response that sends data regularly never trips it. A response that stays silent longer than the limit does.
  • Setting proxy_connect_timeout low (a few seconds) is a good idea. If the upstream is down, you want to fail fast and try another server, rather than hold the visitor for a minute. nginx documents that this timeout usually can't exceed 75 seconds.
  • For a slow endpoint like an export or report, raise the timeout for that location only:
location /reports/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_read_timeout 300s;
}

A better answer for work that takes minutes is a background job and a polling or webhook pattern, not a longer HTTP wait.

Timeouts for PHP-FPM (FastCGI)

For PHP, the equivalents use the fastcgi_ prefix.

location ~ \.php$ {
    include snippets/fastcgi-php.conf;      # Debian/Ubuntu; on other distros include fastcgi_params
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;

    fastcgi_connect_timeout 5s;
    fastcgi_send_timeout    60s;
    fastcgi_read_timeout    60s;   # default 60s; raise only where needed
}

Raising fastcgi_read_timeout is only half the story. PHP has its own limits:

  • max_execution_time in php.ini (default 30 seconds for web requests) stops scripts first.
  • In the PHP-FPM pool file (for example /etc/php/8.2/fpm/pool.d/www.conf), request_terminate_timeout can kill a request after a set time.

If these are lower than nginx's timeout, you get a PHP error or a 502/500 before nginx times out. Align them: for a 5-minute endpoint, raise all three, for that path only if you can.

A very common 502/504 cause on PHP sites is worker exhaustion. The PHP-FPM log says something like server reached pm.max_children setting (5), consider raising it. All workers are busy, new requests queue, and nginx times out. Check:

sudo grep -i "max_children" /var/log/php8.2-fpm.log
sudo systemctl status php8.2-fpm --no-pager | head

Raising pm.max_children helps only if the server has the memory for more workers. Estimate with ps --no-headers -o rss -C php-fpm8.2 | awk '{s+=$1} END {print s/NR/1024 " MB avg"}' and divide spare RAM by that average. Setting it higher than RAM allows causes the kernel to kill processes, which causes more 502s. See also WordPress memory limit exhausted fix for related limits.

Buffer tuning

nginx buffers the response from the upstream. Two situations call for tuning.

"upstream sent too big header"

The first part of the response, the status line and headers, must fit in one buffer. Large cookies, long Set-Cookie headers, or many Link headers can overflow it. This produces a 502.

# proxy_pass upstreams
proxy_buffer_size       16k;
proxy_buffers           8 16k;
proxy_busy_buffers_size 32k;

# FastCGI upstreams
fastcgi_buffer_size       16k;
fastcgi_buffers           16 16k;
fastcgi_busy_buffers_size 32k;

The default buffer size is one memory page (4k or 8k depending on platform). Increase *_buffer_size first, and if you raise *_busy_buffers_size keep it at least as large as the buffer size and smaller than the total of the buffers minus one. Fixing the cause, usually an oversized cookie, is better than only enlarging buffers.

For server-sent events or streaming, turn buffering off for that location with proxy_buffering off;.

Upstream keepalive

By default nginx opens a new connection to the upstream for each request. For high request rates, that costs time and can exhaust local ports. Keepalive reuses connections, and it requires three settings together:

upstream app {
    server 127.0.0.1:3000;
    keepalive 32;               # idle connections kept per worker
    keepalive_timeout 60s;      # idle time before closing (nginx 1.15.3 and later)
    keepalive_requests 1000;    # requests per connection (1.15.3 and later)
}

server {
    location / {
        proxy_pass http://app;
        proxy_http_version 1.1;             # required for keepalive
        proxy_set_header Connection "";     # clear the default "close"
    }
}

The proxy_http_version 1.1 and the empty Connection header are the parts people forget, and without them keepalive silently does nothing.

A known source of intermittent 502s is a keepalive mismatch: the application closes idle connections sooner than nginx expects. nginx sends a request on a connection the upstream has just closed and logs recv() failed (104: Connection reset by peer) or upstream prematurely closed connection. The fix is to make the upstream's idle timeout longer than nginx's keepalive_timeout. For Node's HTTP server, that means raising server.keepAliveTimeout above the nginx value. Check your application server's documentation for its equivalent setting.

For FastCGI, use keepalive in the upstream plus fastcgi_keep_conn on;.

Retries and failover

If you have more than one upstream server, tell nginx when to try the next one:

proxy_next_upstream error timeout http_502 http_503;
proxy_next_upstream_tries 2;
proxy_next_upstream_timeout 10s;

By default nginx does not retry non-idempotent requests (such as POST) after they've been sent, which protects against duplicate orders. Keep it that way. See load balancer health checks for max_fails and fail_timeout.

Test and apply safely

Always test the config, then reload instead of restarting, because reload keeps serving existing connections.

sudo nginx -t
sudo systemctl reload nginx
curl -sI https://example.com/ | head -n 5

Confirm which settings are really active with sudo nginx -T | grep -n proxy_read_timeout, since an include file may override your edit. After the change, measure rather than assume: replay the slow request and watch the access log. If you log upstream timing, you can see where time goes:

log_format timing '$remote_addr "$request" $status '
                  'rt=$request_time urt=$upstream_response_time uct=$upstream_connect_time';
access_log /var/log/nginx/access.log timing;

urt (upstream response time) near your timeout value shows the app is the bottleneck, not nginx.

Don't forget the layer in front of nginx

If a CDN or another proxy sits in front, its timeouts apply too. Cloudflare, for example, returns a 524 when the origin takes longer than its limit (100 seconds by default, per their documentation), regardless of what nginx allows. See Cloudflare 520-526 errors explained. Check the full chain with the HTTP Header Checker, and monitor with the Uptime Monitor, which can alert you when response times climb before errors begin.

Next steps

FAQ

What is the default proxy_read_timeout in nginx?

60 seconds. It is the maximum time between two successive reads from the upstream, not the total time of the response.

Should I just set all timeouts to a very large value?

No. Long timeouts tie up workers and make a failing backend slow down everything. Raise timeouts only for the specific slow paths and fix the underlying slowness.

Why do I get 502 only occasionally under load?

Common causes are worker exhaustion (for example PHP-FPM pm.max_children), upstream keepalive timeouts shorter than nginx's, or the application being killed for memory. The error log line tells you which.

Do I need to restart nginx after changing these directives?

No. Run nginx -t to validate, then systemctl reload nginx. A reload applies the new configuration without dropping active connections.

Get notified when this guide updates

Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.

This page is general information written by our editorial team and updated when documentation changes. Servers and providers differ, so test changes safely first. See our editorial policy and disclaimer.