KeepAlive tuning in Nginx controls how long idle client connections stay open for reuse, reducing handshake overhead and connection churn for sites that serve many small assets. Better reuse usually means lower latency and less CPU spent on new TCP/TLS setup.

HTTP keep-alive works by allowing multiple requests to flow over a single TCP connection instead of closing after each response. In Nginx, the client-side behavior is primarily governed by directives like keepalive_timeout and keepalive_requests, and the practical ceiling is shaped by per-worker limits such as worker_connections and the process file-descriptor limit.

Timeouts set too high can strand a large number of idle sockets and starve workers under high concurrency, while timeouts set too low can increase reconnect rates and TIME_WAIT pressure on clients and intermediaries. Reverse proxy deployments add a second dimension because upstream connection reuse to backends is configured separately from client keep-alive, and values should be aligned with any upstream proxy or load balancer idle timeout to avoid unexpected resets.

Steps to tune KeepAlive in Nginx:

  1. Set client keep-alive limits inside the http block.
    http {
        keepalive_timeout 15s 15s;
        keepalive_requests 1000;
        ##### snipped #####
    }

    The second value in keepalive_timeout adds a Keep-Alive: timeout= header for easier verification with curl.

    Excessively long timeouts can consume file descriptors and worker_connections with idle ESTAB sockets under many concurrent clients.

  2. Enable upstream connection reuse when proxying to HTTP backends.
    upstream app_backend {
        server 127.0.0.1:8080;
        keepalive 32;
    }
    
    server {
        ##### snipped #####
        location /api/ {
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_pass http://app_backend;
        }
    }

    Upstream keepalive reuses connections from Nginx to the backend and is independent of client keep-alive settings.

  3. Test the updated configuration for syntax errors.
    $ sudo nginx -t
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
  4. Reload Nginx to apply the updated keep-alive settings.
    $ sudo systemctl reload nginx

    A reload applies configuration changes without dropping established connections.

  5. Confirm nginx.service is active after the reload.
    $ sudo systemctl status nginx --no-pager
    ● nginx.service - A high performance web server and a reverse proxy server
         Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
         Active: active (running) since Mon 2025-12-01 09:18:22 UTC; 8s ago
           Docs: man:nginx(8)
    ##### snipped #####
  6. Inspect the status page counters to confirm idle keep-alive connections appear as Waiting.
    $ curl -s http://127.0.0.1/nginx_status
    Active connections: 12
    server accepts handled requests
      238 238 912
    Reading: 0 Writing: 1 Waiting: 11

    Higher Waiting usually indicates more idle keep-alive client connections ready for reuse.

  7. Check response headers to confirm the advertised keep-alive timeout when a header timeout is configured.
    $ curl -I http://127.0.0.1/
    HTTP/1.1 200 OK
    Server: nginx
    Date: Mon, 01 Dec 2025 09:19:05 GMT
    Connection: keep-alive
    Keep-Alive: timeout=15
    ##### snipped #####

    If the Keep-Alive header is missing, set the second parameter in keepalive_timeout or rely on Waiting and connection-state monitoring instead.

Discuss the article:

Comment anonymously. Login not required.