Skip to main content
This lesson explains how NGINX handles connections and how to tune key settings for higher throughput and lower latency. We’ll cover worker processes and connections, persistent (keepalive) connections for clients and upstreams, HTTP version considerations, and socket-level optimizations like sendfile and TCP options. Quick overview: NGINX uses a single master process to control multiple worker processes. Each worker runs an event loop that accepts and handles network I/O and client connections independently.
A diagram titled "Connection Handling" showing a master process coordinating multiple worker processes. Incoming request/response traffic is routed to each worker, and each worker runs an event loop to monitor events.

Worker processes and max connections

By default, NGINX chooses one worker process per CPU core (worker_processes auto;), which is generally the recommended configuration. Two directives control concurrency at the worker level:
  • worker_processes — number of NGINX worker processes (recommended: auto).
  • worker_connections — maximum number of connections each worker may open concurrently.
Minimal example:
Approximate maximum simultaneous connections NGINX can handle:
Example: With 4 CPU cores and worker_connections 512, you can expect roughly 4 * 512 = 2048 concurrent connections. Note: worker_connections counts all connections handled by a worker (client connections plus upstream/backend connections and any other socket). Always plan for backend connection usage and file descriptor limits.

Persistent connections (keepalive)

Persistent connections (keepalive) allow clients to reuse the same TCP connection for multiple HTTP requests, avoiding repeated TCP handshakes and reducing CPU and network overhead. Analogy: Opening a new TCP connection for each resource fetch is like queuing for a separate cup of coffee each time. Keepalive lets you stay at the counter and order multiple cups without re-queuing. Without keepalive each resource fetch opens a new connection:
An illustration titled "Without Keep Alive" showing a browser on the left and a server on the right with dashed lines for multiple separate requests/responses. Icons for HTML, XML, JS, CSS and JSON indicate each resource being fetched individually.
With keepalive the browser can reuse the same connection for multiple resources:
A simple network diagram titled "With Keep Alive" showing a browser (laptop) on the left and a server (rack) on the right. Dashed arrows between them illustrate transfer of multiple resource files (JS, HTML, JSON, XML, CSS).
Key keepalive directives (place inside the http block):
  • keepalive_requests — maximum number of requests allowed on a single keepalive connection (default usually 100).
  • keepalive_timeout — number of seconds to keep an idle keepalive connection open.
Example:
When NGINX proxies to upstream servers, it can keep idle connections open to backends to avoid repeated connection setup. Use keepalive inside an upstream block:
In a server/location block, enable HTTP/1.1 proxying and allow NGINX to manage the connection header:
  • proxy_http_version 1.1; — ensures proxy requests use HTTP/1.1 (persistent connections require HTTP/1.1+).
  • proxy_set_header Connection ""; — clears the Connection header so proxying does not inadvertently disable keepalive.
You can inspect the negotiated HTTP version in your browser DevTools Network tab or with curl:
Example output:

HTTP protocol evolution and impact

A short history of HTTP and how it affects connection handling:
  • HTTP/0.9 — minimal protocol, no headers.
  • HTTP/1.0 — added status codes and headers.
  • HTTP/1.1 — persistent connections, chunked encoding, improved caching.
  • HTTP/2 — multiplexing multiple requests/responses over a single connection, header compression, better utilization of a single TCP connection.
  • HTTP/3 — runs over QUIC/UDP, improves latency and loss recovery and requires TLS 1.3.
An illustration titled "HTTP Versions" that shows the evolution of the HTTP protocol as five silhouettes progressing from a hunched, ape-like figure (HTTP 0.9) to a modern upright humanoid (HTTP 3.0). Each figure is labeled with its HTTP version (0.9, 1.0, 1.1, 2.0, 3.0).
Transport layer summary:
  • TCP — connection-oriented, reliable, used by HTTP/1.1 and HTTP/2 (though HTTP/2 improvements are at the protocol layer).
  • UDP/QUIC — used by HTTP/3 for lower-latency recovery and faster connection establishment.
Adoption varies; many sites use HTTP/2 and HTTP/3 while others remain on HTTP/1.1.
A slide with two side-by-side line charts titled "HTTP Versions Usage," showing historical usage trends for HTTP/2 (about 34.6%) and HTTP/3 (about 34.0%) across 2024–2025. The graphs plot monthly percentage changes for each protocol.

Zero-copy file transfer: sendfile

Serving static files benefits from kernel-level zero-copy using sendfile(). Instead of copying file data into user-space buffers and then sending to the socket, NGINX can ask the kernel to transfer data directly from disk to the network socket. Enable sendfile in the http block:
This reduces CPU usage and memory copying when serving static assets.

TCP socket options: tcp_nopush and tcp_nodelay

NGINX exposes socket-level options to control small-packet behavior:
  • tcp_nopush on; — when using sendfile, delays sending response headers until the first body chunk is ready so headers and the first body chunk can be sent in fewer packets. Useful for reducing packet count with large static responses.
  • tcp_nodelay on; — disables Nagle’s algorithm so that small packets are sent immediately (reduces latency for small writes).
Typical configuration:

Quick reference table

Practical tuning checklist

  • Start with worker_processes auto and measure.
  • Calculate expected max connections and ensure the OS file descriptor limits are high enough.
  • Use keepalive for upstream pools to reduce backend connection churn.
  • Enable sendfile when serving static assets.
  • Use tcp_nopush with sendfile and tcp_nodelay for appropriate latency/throughput tradeoffs.
  • Test with realistic traffic patterns, monitoring CPU, memory, and file descriptor usage.
Be careful when increasing keepalive and worker connection limits: keeping many idle connections consumes file descriptors and memory. Monitor file descriptor limits (ulimit -n), CPU, and memory, and tune values according to your workload and testing results.
Clearing or setting the Connection header incorrectly in proxying can disable keepalive. Use proxy_set_header Connection ""; to let NGINX manage connection persistence, and confirm behavior in staging before production.

References and further reading

This concludes the lesson on connection handling and core NGINX tuning options for connections, keepalive, and socket-level optimizations.

Watch Video