HTTP

Header

Keep-Alive Header

Learn how the Keep-Alive header controls HTTP connection persistence and reuse. Reduce latency and improve performance by avoiding repeated TCP handshakes.

5 min read intermediate Try in Playground

What is Keep-Alive?

TL;DR: Provides parameters for persistent connections like timeout and max requests. Helps optimize connection reuse for better performance.

The Keep-Alive header allows the client and server to negotiate parameters for persistent connections. It enables multiple HTTP requests and responses to be sent over a single TCP connection, reducing latency and improving performance.

While HTTP/1.1 uses persistent connections by default, the Keep-Alive header provides fine-grained control over connection behavior, such as timeout and maximum requests.

How Keep-Alive Works

Client requests persistent connection:

GET /api/users HTTP/1.1
Host: api.example.com
Connection: keep-alive
Keep-Alive: timeout=5, max=100
```text

**Server acknowledges:**

```http
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: timeout=5, max=100
Content-Type: application/json

{"users": [...]}

Syntax

Keep-Alive: timeout=<seconds>
Keep-Alive: max=<requests>
Keep-Alive: timeout=<seconds>, max=<requests>
```text

### Parameters

- **timeout** - Time in seconds the connection should remain open when idle
- **max** - Maximum number of requests that can be sent on this connection before closing

## Common Examples

### Basic Keep-Alive

```http
Connection: keep-alive
Keep-Alive: timeout=5

Keeps connection open for 5 seconds of idle time.

With Maximum Requests

Connection: keep-alive
Keep-Alive: timeout=10, max=1000
```text

Keeps connection open for 10 seconds, allows up to 1000 requests.

### Long-Lived Connection

```http
Connection: keep-alive
Keep-Alive: timeout=120, max=5000

Suitable for applications with frequent requests.

Real-World Scenarios

API Gateway Configuration

# Client to API Gateway
GET /api/products HTTP/1.1
Host: gateway.example.com
Connection: keep-alive
Keep-Alive: timeout=60, max=1000

# Gateway to Client
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: timeout=60, max=1000
Content-Type: application/json
```text

### High-Traffic Web Server

```http
# Server response
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: timeout=15, max=500
Content-Type: text/html

Balances connection reuse with resource management.

Microservices Communication

GET /internal/user-service/profile HTTP/1.1
Host: internal.services
Connection: keep-alive
Keep-Alive: timeout=30, max=2000
```text

Internal services benefit from long-lived connections.

## HTTP/1.0 vs HTTP/1.1

### HTTP/1.0 (Explicit Keep-Alive)

```http
# Client must explicitly request
GET / HTTP/1.0
Connection: keep-alive
Keep-Alive: timeout=5, max=100

# Server must explicitly agree
HTTP/1.0 200 OK
Connection: keep-alive
Keep-Alive: timeout=5, max=100

HTTP/1.1 (Default Persistent)

# Persistent by default, no header needed
GET / HTTP/1.1
Host: example.com

# Server can override
HTTP/1.1 200 OK
Keep-Alive: timeout=5, max=100
```text

## Closing Connections

### Client Closes Connection

```http
GET /api/logout HTTP/1.1
Host: api.example.com
Connection: close

Explicitly closes after response.

Server Signals Last Response

HTTP/1.1 200 OK
Connection: close
Content-Type: application/json

{"message": "Goodbye"}
```javascript

Server indicates connection will close after this response.

## Server Configuration Examples

### Node.js (Express)

```javascript
const express = require('express')
const app = express()

// Configure keep-alive settings
const server = app.listen(3000, () => {
  server.keepAliveTimeout = 65000 // 65 seconds
  server.headersTimeout = 66000 // Slightly higher than keep-alive
})

// Set keep-alive header in responses
app.use((req, res, next) => {
  res.set('Keep-Alive', 'timeout=65, max=1000')
  next()
})

app.get('/api/data', (req, res) => {
  res.json({ data: 'example' })
})

Nginx

http {
    keepalive_timeout 65;        # 65 seconds timeout
    keepalive_requests 1000;     # Max 1000 requests per connection

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";  # Enable keep-alive to backend
        }
    }
}
```text

### Apache

```apache
# Enable keep-alive
KeepAlive On

# Maximum requests per connection
MaxKeepAliveRequests 100

# Timeout in seconds
KeepAliveTimeout 5

Client Implementation

JavaScript (Fetch API)

// Fetch API uses keep-alive by default
fetch('https://api.example.com/data', {
  headers: {
    Connection: 'keep-alive'
  }
})
  .then((response) => response.json())
  .then((data) => console.log(data))
```javascript

### Node.js HTTP Agent

```javascript
const http = require('http')

// Create agent with keep-alive enabled
const agent = new http.Agent({
  keepAlive: true,
  keepAliveMsecs: 30000, // 30 seconds
  maxSockets: 50,
  maxFreeSockets: 10
})

// Use agent for requests
http.get(
  {
    hostname: 'api.example.com',
    path: '/data',
    agent: agent
  },
  (res) => {
    res.on('data', (chunk) => console.log(chunk.toString()))
  }
)

Python (requests)

import requests
from requests.adapters import HTTPAdapter

# Create session with connection pooling (keep-alive)
session = requests.Session()
adapter = HTTPAdapter(pool_connections=10, pool_maxsize=100)
session.mount('https://', adapter)

# Reuses connections automatically
response1 = session.get('https://api.example.com/users')
response2 = session.get('https://api.example.com/posts')
response3 = session.get('https://api.example.com/comments')
```text

### cURL

```bash
# cURL uses keep-alive by default in HTTP/1.1
curl https://api.example.com/data

# Explicitly disable keep-alive
curl -H "Connection: close" https://api.example.com/data

# View connection details
curl -v https://api.example.com/data

Performance Benefits

Without Keep-Alive

Request 1: TCP handshake → HTTP request → Response → Close
Request 2: TCP handshake → HTTP request → Response → Close
Request 3: TCP handshake → HTTP request → Response → Close

Total: 3 TCP handshakes (3-way handshake each = 9 round trips)

With Keep-Alive

Connection: TCP handshake →
Request 1 → Response →
Request 2 → Response →
Request 3 → Response →
Close

Total: 1 TCP handshake (3 round trips)

Saves significant latency, especially with TLS (which adds more handshake overhead).

Best Practices

For Servers

1. Set reasonable timeouts

# ✅ Balanced timeout
Keep-Alive: timeout=30, max=1000

# ❌ Too short (defeats purpose)
Keep-Alive: timeout=1, max=10

# ❌ Too long (wastes resources)
Keep-Alive: timeout=300, max=10000
```javascript

**2. Consider server load**

```javascript
// Adjust based on traffic
const keepAliveTimeout = highTraffic ? 15000 : 60000
server.keepAliveTimeout = keepAliveTimeout

3. Monitor connection limits

const server = app.listen(3000)

// Prevent resource exhaustion
server.maxConnections = 1000

setInterval(() => {
  console.log('Active connections:', server.connections)
}, 5000)
```javascript

### For Clients

**1. Reuse connections with connection pooling**

```javascript
// ✅ Use connection pooling
const agent = new http.Agent({ keepAlive: true })

// ❌ Create new connection each time
const agent = new http.Agent({ keepAlive: false })

2. Close connections when done

// Graceful shutdown
process.on('SIGTERM', () => {
  server.close(() => {
    console.log('Server closed')
    process.exit(0)
  })
})
```text

**3. Handle connection errors**

```javascript
agent.on('error', (err) => {
  console.error('Agent error:', err)
})

Common Issues

Proxy Complications

# Client → Proxy
Connection: keep-alive
Keep-Alive: timeout=60

# Proxy might not forward Keep-Alive to origin
# Or might use different settings
```text

Some proxies strip or modify Keep-Alive headers.

### Load Balancer Timeouts

```nginx
# Load balancer timeout must be > backend timeout
proxy_connect_timeout 70s;
proxy_send_timeout 70s;
proxy_read_timeout 70s;

# Backend keep-alive timeout
keepalive_timeout 65s;

Connection Pool Exhaustion

// ✅ Limit pool size
const agent = new http.Agent({
  keepAlive: true,
  maxSockets: 50, // Limit concurrent connections
  maxFreeSockets: 10 // Limit idle connections
})
```text

## HTTP/2 and HTTP/3

Keep-Alive is not used in HTTP/2 and HTTP/3:

- **HTTP/2** uses multiplexing over a single connection
- **HTTP/3** uses QUIC protocol with built-in connection management

```http
# HTTP/2 - No Keep-Alive header needed
:method: GET
:path: /api/data
:scheme: https
:authority: example.com

Security Considerations

Connection Timeout Attacks

// Prevent slowloris attacks
server.keepAliveTimeout = 65000
server.headersTimeout = 66000 // Must be > keepAliveTimeout
server.requestTimeout = 300000 // Maximum request time
```text

### Resource Limits

```nginx
# Limit connections per IP
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn addr 10;

# Prevent too many idle connections
keepalive_timeout 30;
keepalive_requests 100;

Frequently Asked Questions

What is the Keep-Alive header?

Keep-Alive provides parameters for persistent connections: timeout (seconds to keep open) and max (maximum requests). Used with Connection: keep-alive.

Is Keep-Alive still relevant?

Less so in HTTP/2 and HTTP/3 which manage connections differently. In HTTP/1.1, it helps clients understand connection limits.

What does timeout parameter mean?

timeout=5 means the server will keep the connection open for 5 seconds of inactivity. After that, it may close the connection.

What does max parameter mean?

max=100 means the connection can be reused for up to 100 requests. After that, a new connection is needed. Helps servers manage resources.

Keep Learning