2026年11月12日

2026年11月12日

Nginx Upstream Keepalive: How to Reuse Backend Connections

This article is a localized edition of the Japanese original. Read the Japanese original

When you use Nginx as a reverse proxy or load balancer, Nginx connects to the backend (an application server or PHP-FPM) for each client request. Setting keepalive in the upstream block makes Nginx keep finished connections open instead of closing them, so they can be reused for the next request. This cuts the TCP handshake needed for each connection (and the TLS handshake too if the backend uses HTTPS), and keeps TIME_WAIT connections from piling up on the backend.

This article was checked against the official Nginx documentation as of October 2026 (stable 1.30.6, mainline 1.31.8).

The keepalive number is not “seconds”

First, a common mistake. The number you put after keepalive in an upstream block is not a number of seconds; it is the maximum number of idle connections each worker process keeps.

Directive (inside upstream) Meaning Default
keepalive connections Maximum number of idle connections each worker keeps. When exceeded, the least recently used ones are closed 32 local since 1.29.7. Not set (no connections kept) before that
keepalive_timeout time How long an idle connection stays open 60s (since 1.15.3)
keepalive_requests number Maximum number of requests served over one connection 1000 (100 before 1.19.10)
keepalive_time time Maximum time one connection can be used 1h (since 1.19.10)

keepalive does not limit the total number of connections a worker opens. If you set it too high, the backend has no room left to accept new connections, so keep it well below the backend's limit on concurrent connections.

Keepalive between clients (browsers) and Nginx is a separate setting, such as keepalive_timeout in the http or server block (default 75 seconds). This article covers keepalive between Nginx and the backend.

The defaults depend on the version

Nginx 1.29.7 (released March 2026) changed these defaults:

  • Upstream keepalive is enabled by default (keepalive 32 local;).
  • The default proxy_http_version changed from 1.0 to 1.1.
  • The Connection header is no longer sent to the proxied server by default.

So in 1.29.7 and later (the 1.30 series on the stable branch), HTTP proxying through an upstream block reuses connections without any extra configuration. On the other hand, the stock packages in Ubuntu 24.04 (1.24) and Debian 13 (1.26), both as of October 2026, predate this change. With those versions you need the settings in the next section.

Check which version you are running with:

nginx -v

Configuration (HTTP backends)

To use upstream keepalive on 1.29.6 or earlier, you need all three of these:

  • Add keepalive to the upstream block.
  • Add proxy_http_version 1.1; (connections cannot be reused with HTTP/1.0).
  • Add proxy_set_header Connection ""; so that Connection: close is not sent.

Example configuration (put it somewhere loaded inside the http block, such as /etc/nginx/conf.d/backend.conf):

upstream backend {
    server 192.0.2.11:8080;
    server 192.0.2.12:8080;

    keepalive 16;             # Max idle connections kept per worker (not seconds)
    keepalive_timeout 60s;    # How long an idle connection stays open
    keepalive_requests 1000;  # Max requests per connection
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;          # Default since 1.29.7, can be omitted
        proxy_set_header Connection "";  # Not needed since 1.29.7
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

If you use a load-balancing method other than round-robin, such as least_conn, ip_hash or hash, on 1.29.6 or earlier it must appear before the keepalive line.

upstream backend {
    least_conn;               # Put the balancing method before keepalive
    server 192.0.2.11:8080;
    server 192.0.2.12:8080;
    keepalive 16;
}

On 1.29.7 and later, write keepalive only if you want a number other than the default 32. keepalive 0; disables keepalive to the upstream.

Using it with PHP-FPM (FastCGI)

For FastCGI backends you need fastcgi_keep_conn on; in addition to upstream keepalive. This is still true in 1.29.7 and later.

upstream php_fpm {
    server unix:/run/php/php8.3-fpm.sock;
    keepalive 8;
}

server {
    listen 80;
    server_name example.com;
    root /var/www/example.com;
    index index.php;

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass php_fpm;
        fastcgi_keep_conn on;
    }
}

The socket path depends on the PHP version and the OS (the example is PHP 8.3 on Ubuntu 24.04).

A PHP-FPM child process stays dedicated to one connection while it is serving it. Every idle connection Nginx keeps ties up a child process, so choose keepalive so that "number of workers × keepalive" stays well below pm.max_children. If child processes run out, new requests have to wait.

Apply and verify the settings

# Check the syntax before applying
sudo nginx -t
sudo systemctl reload nginx

Check whether connections are being reused with the ss command (assuming the backend listens on port 8080).

# On the Nginx server: established connections to the backend
# With keepalive working, connections remain for keepalive_timeout even after requests stop
ss -tn state established '( dport = :8080 )'

# On the backend server: number of TIME_WAIT connections (the first line is a header)
# Without keepalive, this grows with the number of requests
ss -tn state time-wait '( sport = :8080 )' | wc -l

Without keepalive, the connection is closed after every request. With HTTP/1.0 it is usually the backend that closes the connection first, so the TIME_WAIT entries pile up on the backend side. In a test with Nginx 1.24 (one worker), 50 requests without keepalive left 50 TIME_WAIT connections on the backend side, while with keepalive configured only one established connection remained. With Nginx 1.30, the result was the same as the keepalive case even without writing any keepalive settings.

If TIME_WAIT does not go down, check that the proxy_http_version and Connection header settings are not missing. Errors can be checked in /var/log/nginx/error.log or with journalctl -u nginx.

Notes

  • Backend idle timeout: if the backend closes idle connections sooner than Nginx's keepalive_timeout, Nginx may try to use a connection the backend has just closed, causing an error. Make the backend's timeout longer, or shorten keepalive_timeout on the Nginx side.
  • Do not make keepalive_requests too large: the official documentation says connections must be closed periodically to free per-connection memory, and too high a value can lead to excessive memory usage.
  • The local parameter (1.29.7 and later): with the default keepalive 32 local;, kept connections are not shared with other location blocks. Without local, connections to the same backend can be reused across locations.

Basic reverse proxy setup is covered in How to set up a reverse proxy with Nginx, and PHP-FPM integration in How to configure Nginx with PHP-FPM.

Summary

  • The upstream keepalive number is the maximum number of idle connections each worker keeps. Seconds are set with keepalive_timeout.
  • On 1.29.6 and earlier, you need proxy_http_version 1.1; and proxy_set_header Connection ""; in addition to keepalive.
  • On 1.29.7 and later it is enabled by default for HTTP proxying. The stock OS packages (Ubuntu 24.04, Debian 13) are older than that.
  • PHP-FPM needs fastcgi_keep_conn on;, and the number of kept connections should be chosen with pm.max_children in mind.
  • Verify the effect with ss: established connections that remain on the Nginx side and the number of TIME_WAIT entries on the backend side.

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲