Linux VPS

Keep-alive and timeouts: tune Nginx, Apache and PHP-FPM together

What HTTP keep-alive does, every timeout between the browser and PHP, how to align them so you avoid 502/504 errors and slowloris-style attacks, and safe starting values.

AdvancedLinux VPS3 min read

Keep-alive lets a browser reuse one TCP (and TLS) connection for many requests instead of opening a new one for every file. Timeouts decide how long each layer waits before giving up. Tuning keep-alive and timeouts together makes a server faster, more resistant to slow-client attacks, and stops confusing 502 and 504 errors.

Before you start

  • Root access to a server running Nginx or Apache, usually with PHP-FPM.
  • Know your longest legitimate request (imports, report exports, uploads).

Why keep-alive matters

A new HTTPS connection costs a TCP handshake plus a TLS handshake, one or more round trips before the first byte. Keep-alive pays that once per visitor. HTTP/2 goes further and multiplexes all requests over one connection, so the keep-alive timeout simply controls how long an idle connection stays open.

The cost: each idle connection holds memory, and on Apache prefork a whole process. That is why the right value differs by server.

The timeout chain

LayerSettingWhat it limits
Nginxclient_header_timeout, client_body_timeoutSlow clients sending headers or upload body
Nginxkeepalive_timeoutIdle time between requests on a connection
Nginxsend_timeoutGap between two writes to a slow client
Nginxfastcgi_read_timeout / proxy_read_timeoutWaiting for PHP-FPM or a backend to answer
ApacheKeepAliveTimeout, Timeout, RequestReadTimeoutIdle keep-alive, I/O waits, slow headers/body
PHP-FPMrequest_terminate_timeoutHard kill of a running PHP request
PHPmax_execution_timeScript CPU time limit

Align the timeouts

The rule: each inner layer should give up before the outer layer. Then the user gets a clear error from the layer that actually failed.

  • max_execution_time (e.g. 60) < request_terminate_timeout (e.g. 70) < fastcgi_read_timeout (e.g. 75).
  • If Nginx gives up first, you get 504 Gateway Timeout while PHP keeps running in the background and wastes a worker.
  • If PHP-FPM kills the request first, Nginx logs "upstream prematurely closed connection" and returns 502.

For a single slow admin endpoint, raise timeouts in its own location rather than globally.

Starting values for Nginx

keepalive_timeout 30s;
keepalive_requests 1000;
client_header_timeout 15s;
client_body_timeout 30s;
send_timeout 30s;
fastcgi_read_timeout 75s;

Nginx handles idle connections cheaply, so 30–65 seconds of keep-alive is fine.

Starting values for Apache

KeepAlive On
MaxKeepAliveRequests 500
KeepAliveTimeout 5
Timeout 60
RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500

With the event MPM a longer KeepAliveTimeout is cheap; with prefork keep it at 2–5 seconds so idle visitors do not occupy processes. RequestReadTimeout (mod_reqtimeout) is Apache's main defence against slowloris attacks.

Upstream keep-alive

Nginx can also keep connections open to backends, which helps when proxying to Node.js or another HTTP service:

upstream app { server 127.0.0.1:3000; keepalive 32; }
location / { proxy_pass http://app; proxy_http_version 1.1; proxy_set_header Connection ""; }

Verify

  1. Check that connections are reused: curl -sv https://example.com/ https://example.com/robots.txt 2>&1 | grep -i 're-using\|Connected'.
  2. Watch connection states: ss -s and ss -tan state established | wc -l.
  3. Look for timeouts in error logs after changes.

Troubleshooting

  • 504 on long imports: raise fastcgi_read_timeout and PHP limits for that location only, or move the job to a background queue.
  • Thousands of idle connections on Apache prefork: lower KeepAliveTimeout or switch to the event MPM.
  • Uploads fail midway: client_body_timeout too short for slow connections.

FAQ: keep-alive and timeouts

Should I disable keep-alive?

Almost never. It is a large speed win. Only shorten it on memory-starved prefork servers.

Does HTTP/2 need keep-alive settings?

Yes, keepalive_timeout still closes idle HTTP/2 connections in Nginx.

Why does my site return 504 but the task still finishes?

Nginx stopped waiting before PHP finished. Align the timeout chain.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.