Backend Development 14 min read

How Keep‑Alive Works in HTTP and TCP: Deep Dive into Nginx, Node.js and Linux

This article examines the keep‑alive mechanism at both the HTTP and TCP layers, explains how Nginx parses and processes the Connection header, dissects Linux kernel socket options and libuv implementation, and shows how to configure and debug keep‑alive to release idle connections efficiently.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
How Keep‑Alive Works in HTTP and TCP: Deep Dive into Nginx, Node.js and Linux

Introduction

The article explores why long‑lived connections can improve efficiency but also waste resources when the peer disappears, and it investigates how to detect dead connections using keep‑alive mechanisms in HTTP and TCP.

1. HTTP Layer Keep‑Alive

When a client sends

Connection: keep-alive

, Nginx keeps the connection open for a configurable period and limits the number of requests per connection.

<code>keepalive_timeout timeout;
keepalive_requests number;</code>

Nginx reads the request header with

ngx_http_read_request_header

, parses the request line, and extracts the

Connection

header. The header is stored in

r-&gt;header_in

. Based on the header value, Nginx sets

r-&gt;headers_in.connection_type

to either

NGX_HTTP_CONNECTION_CLOSE

or

NGX_HTTP_CONNECTION_KEEP_ALIVE

, and later the

keepalive

flag is set accordingly.

<code>static void ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, unsigned offset) {
    if (ngx_strcasestrn(h-&gt;value.data, "close", 5)) {
        r-&gt;headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
    } else if (ngx_strcasestrn(h-&gt;value.data, "keep-alive", 10)) {
        r-&gt;headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;
    }
}</code>

After processing the request,

ngx_http_finalize_connection

decides whether to close the socket or start a keep‑alive timer with

ngx_http_set_keepalive

.

<code>if (r-&gt;keepalive && clcf-&gt;keepalive_timeout &gt; 0) {
    ngx_http_set_keepalive(r);
}</code>

2. TCP Layer Keep‑Alive

TCP provides three configurable parameters: idle time before probes, interval between probes, and the maximum number of probes. Linux defaults are defined as:

<code>#define TCP_KEEPALIVE_TIME   (120 * 60 * HZ)   // two hours
#define TCP_KEEPALIVE_PROBES 9
#define TCP_KEEPALIVE_INTVL (75 * HZ)</code>

These values correspond to

TCP_KEEPIDLE

,

TCP_KEEPINTVL

, and

TCP_KEEPCNT

socket options.

3. Nginx Implementation of Keep‑Alive

Nginx uses the socket option

SO_KEEPALIVE

to enable the TCP keep‑alive mechanism. When enabled, it also sets the TCP‑level options via

setsockopt

if the application provides values.

<code>int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &amp;on, sizeof(on)))
        return UV__ERR(errno);
    if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &amp;delay, sizeof(delay)))
        return UV__ERR(errno);
    return 0;
}</code>

In the Linux kernel,

sock_setsockopt

handles

SO_KEEPALIVE

by calling

tcp_set_keepalive

, which updates

tp-&gt;keepalive_time

and starts a timer if the socket is in an established state.

<code>if (sock_flag(sk, SOCK_KEEPOPEN) &&
    !(TCPF_CLOSE | TCPF_LISTEN) &amp; (1 &lt;&lt; sk-&gt;sk_state)) {
    tp-&gt;keepalive_time = val * HZ;
    tcp_reset_keepalive_timer(sk, keepalive_time_when(tp));
}</code>

The timer callback

tcp_keepalive_timer

checks how long the connection has been idle. If the idle period exceeds

keepalive_time

, it sends a probe. After a configurable number of unanswered probes (

keepalive_probes

), it sends a reset packet and closes the connection.

<code>if (elapsed &gt;= keepalive_time_when(tp)) {
    if (tp-&gt;probes_out &gt;= sysctl_tcp_keepalive_probes) {
        tcp_send_active_reset(sk, GFP_ATOMIC);
        tcp_write_err(sk);
    } else {
        tcp_write_wakeup(sk);
        tp-&gt;probes_out++;
    }
    tcp_reset_keepalive_timer(sk, keepalive_intvl_when(tp));
}</code>

4. Interaction with Application Data

If the socket has pending data or unacknowledged packets, the kernel prefers sending those over keep‑alive probes, which can make the heartbeat ineffective. To handle this, Linux provides

TCP_USER_TIMEOUT

, which forces the connection to be closed if no ACK is received within a user‑defined interval.

<code>if (tp-&gt;user_timeout &&
    (tp-&gt;packets_out &gt; 1) &&
    (tcp_time_stamp - tp-&gt;rcv_tstamp) &gt;= tp-&gt;user_timeout)
    tcp_send_active_reset(sk, GFP_ATOMIC);
</code>

Setting both

SO_KEEPALIVE

and

TCP_USER_TIMEOUT

ensures that idle connections are detected reliably even when application traffic is present.

5. Default Settings and Verification

On a typical Linux system, keep‑alive is disabled by default. The article provides a small C program that creates a socket, queries

SO_KEEPALIVE

,

TCP_KEEPIDLE

,

TCP_KEEPINTVL

, and

TCP_KEEPCNT

, and prints their values.

<code>int sockfd = socket(AF_INET, SOCK_STREAM, 0);
getsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &amp;optval, &amp;optlen);
printf("keep‑alive enabled: %d\n", optval);
getsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, &amp;optval, &amp;optlen);
printf("idle time: %d seconds\n", optval);
getsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, &amp;optval, &amp;optlen);
printf("probe interval: %d seconds\n", optval);
getsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, &amp;optval, &amp;optlen);
printf("max probes: %d\n", optval);
</code>

The output confirms that keep‑alive is off and shows the default timeout values.

6. Observing Keep‑Alive Packets

Using Wireshark, a keep‑alive probe appears as a TCP segment with the ACK flag set and no payload. The article includes screenshots of such packets.

Wireshark keep‑alive packet
Wireshark keep‑alive packet

In summary, the keep‑alive mechanism involves coordinated configuration at both the HTTP and TCP layers. Properly tuning

keepalive_timeout

,

keepalive_requests

,

TCP_KEEPIDLE

,

TCP_KEEPINTVL

,

TCP_KEEPCNT

, and optionally

TCP_USER_TIMEOUT

allows a server to reuse connections efficiently while avoiding resource waste caused by dead peers.

TCPLinuxkeep-alivenginxnodejs
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.