- Home
- HTTP Headers
- Connection Header
Header
Connection Header
Learn how the Connection header controls whether HTTP connections stay open (keep-alive) or close after each request. Optimize with persistent connections.
Connection Header
TL;DR: Controls whether the network connection stays open after the current transaction. Use keep-alive for reuse or close to end the connection immediately.
What is Connection?
The Connection header controls whether the network connection stays open after the current HTTP transaction completes. It’s like deciding whether to keep a phone line open for more conversation or hang up after each call.
In modern HTTP (HTTP/1.1+), connections are persistent by default, meaning they stay open for multiple requests. The Connection header can override this behavior.
How Connection Works
HTTP/1.1 - Persistent by default:
GET /page1 HTTP/1.1
Host: example.com
Connection: keep-alive
```text
**Server confirms:**
```http
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 1234
[response body]
The connection stays open for the next request.
Close connection after response:
GET /page1 HTTP/1.1
Host: example.com
Connection: close
```text
**Server responds and closes:**
```http
HTTP/1.1 200 OK
Connection: close
Content-Length: 1234
[response body]
The connection closes after this response.
Syntax
Connection: keep-alive
Connection: close
Connection: upgrade
```text
### Common Values
```http
# Keep connection open (default in HTTP/1.1)
Connection: keep-alive
# Close connection after response
Connection: close
# Upgrade to different protocol (WebSocket, HTTP/2)
Connection: upgrade
# Multiple directives (comma-separated)
Connection: keep-alive, Upgrade
HTTP Version Differences
HTTP/1.0
# Connections close by default
GET / HTTP/1.0
Host: example.com
# Must explicitly request keep-alive
GET / HTTP/1.0
Host: example.com
Connection: keep-alive
```text
### HTTP/1.1
```http
# Connections persist by default
GET / HTTP/1.1
Host: example.com
# Equivalent to:
GET / HTTP/1.1
Host: example.com
Connection: keep-alive
HTTP/2 and HTTP/3
# Connection header is ignored
# HTTP/2+ uses multiplexed streams over a single connection
# Connection management is built into the protocol
```text
## Common Examples
### Normal Persistent Connection
```http
GET /api/users HTTP/1.1
Host: api.example.com
Connection: keep-alive
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: timeout=5, max=100
Content-Length: 1234
{"users": [...]}
Connection stays open for up to 5 seconds or 100 requests.
Explicit Connection Close
GET /api/logout HTTP/1.1
Host: api.example.com
Connection: close
HTTP/1.1 200 OK
Connection: close
Content-Length: 25
{"message": "Logged out"}
```http
Connection closes after logout.
### WebSocket Upgrade
```http
GET /ws HTTP/1.1
Host: websocket.example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Connection upgraded to WebSocket protocol.
Real-World Scenarios
High-Performance API
Client makes multiple requests:
// All these requests reuse the same connection
async function fetchUserData() {
const profile = await fetch('https://api.example.com/profile')
const posts = await fetch('https://api.example.com/posts')
const comments = await fetch('https://api.example.com/comments')
// Browser automatically manages connection pooling
// Connection: keep-alive is implicit in HTTP/1.1
}
```javascript
**Benefits:**
- No TCP handshake overhead for each request
- Reduced latency
- Better throughput
### Graceful Server Shutdown
```javascript
// Node.js/Express
let isShuttingDown = false
process.on('SIGTERM', () => {
isShuttingDown = true
console.log('Server shutting down gracefully...')
})
app.use((req, res, next) => {
if (isShuttingDown) {
// Tell clients to close connection
res.setHeader('Connection', 'close')
}
next()
})
app.get('/api/data', (req, res) => {
res.json({ data: 'value' })
})
Load Balancer Health Checks
app.get('/health', (req, res) => {
// Health checks should close connection immediately
res.setHeader('Connection', 'close')
res.json({ status: 'healthy' })
})
```javascript
### Long-Polling with Connection Management
```javascript
app.get('/events', async (req, res) => {
// Keep connection alive for long-polling
res.setHeader('Connection', 'keep-alive')
res.setHeader('Keep-Alive', 'timeout=60')
// Wait for events (simplified)
const event = await waitForEvent(60000)
if (event) {
res.json(event)
} else {
res.status(204).end()
}
})
Keep-Alive Parameters
The Keep-Alive header (separate from Connection) provides parameters:
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: timeout=5, max=100
Content-Length: 1234
```javascript
- `timeout=5`: Keep connection idle for 5 seconds
- `max=100`: Allow max 100 requests on this connection
```javascript
// Node.js server configuration
const server = http.createServer(app)
server.keepAliveTimeout = 5000 // 5 seconds
server.maxHeadersCount = 100 // max 100 requests
Best Practices
1. Let HTTP/1.1 Use Default Persistence
// ❌ Unnecessary - keep-alive is default in HTTP/1.1
res.setHeader('Connection', 'keep-alive')
// ✅ Only set when you need to override default behavior
if (shuttingDown) {
res.setHeader('Connection', 'close')
}
```text
### 2. Close Connections for Final Requests
```javascript
// ✅ Close connection after logout
app.post('/logout', (req, res) => {
req.session.destroy()
res.setHeader('Connection', 'close')
res.json({ message: 'Logged out' })
})
// ✅ Close connection after delete account
app.delete('/account', (req, res) => {
deleteUserAccount(req.user.id)
res.setHeader('Connection', 'close')
res.json({ message: 'Account deleted' })
})
3. Handle Connection Timeouts
// ✅ Configure appropriate timeouts
const server = http.createServer(app)
// Idle timeout for keep-alive connections
server.keepAliveTimeout = 65000 // 65 seconds (slightly longer than load balancer)
// Overall request timeout
server.requestTimeout = 120000 // 120 seconds
```text
### 4. Don't Use Connection Header in HTTP/2
```javascript
// ❌ Connection header is ignored in HTTP/2
// HTTP/2 manages connections differently
// ✅ Detect HTTP version
app.use((req, res, next) => {
if (req.httpVersion === '1.1' && shouldCloseConnection) {
res.setHeader('Connection', 'close')
}
next()
})
Connection Pooling
Client-Side (Browser)
// Browsers automatically pool connections
// Default: 6-8 connections per origin
// Multiple parallel requests share connections
Promise.all([fetch('/api/users'), fetch('/api/posts'), fetch('/api/comments'), fetch('/api/likes')])
// These 4 requests may use 2-4 connections from the pool
```javascript
### Node.js Client
```javascript
const http = require('http')
// Configure connection pooling
const agent = new http.Agent({
keepAlive: true,
keepAliveMsecs: 1000,
maxSockets: 50, // max 50 connections per host
maxFreeSockets: 10, // max 10 idle connections
timeout: 60000 // 60 second timeout
})
// Use the agent
fetch('https://api.example.com/data', {
agent: agent
})
Debugging Connection Issues
Check Connection Header
# Curl shows connection management
curl -v https://api.example.com/data
# Look for:
# < Connection: keep-alive
# < Keep-Alive: timeout=5, max=100
```text
### Monitor Connection Reuse
```javascript
// Chrome DevTools
// Network tab → Right-click header → add "Connection ID"
// Same Connection ID = reused connection
Server-Side Logging
app.use((req, res, next) => {
const connectionId = req.socket.remotePort
console.log(`Request on connection ${connectionId}`)
res.on('finish', () => {
console.log(`Response sent, connection ${connectionId}`)
})
req.socket.on('close', () => {
console.log(`Connection ${connectionId} closed`)
})
next()
})
```javascript
## Common Patterns
### Graceful Shutdown Pattern
```javascript
let server
let isShuttingDown = false
function startServer() {
server = app.listen(3000)
server.keepAliveTimeout = 65000
}
function gracefulShutdown() {
isShuttingDown = true
// Stop accepting new connections
server.close(() => {
console.log('Server shut down')
process.exit(0)
})
// Force close after 30 seconds
setTimeout(() => {
console.error('Forcing shutdown')
process.exit(1)
}, 30000)
}
process.on('SIGTERM', gracefulShutdown)
process.on('SIGINT', gracefulShutdown)
app.use((req, res, next) => {
if (isShuttingDown) {
res.setHeader('Connection', 'close')
res.status(503).json({ error: 'Server shutting down' })
} else {
next()
}
})
Load Balancer-Aware Configuration
// AWS ALB idle timeout is 60 seconds
// Set server keep-alive slightly longer
const server = http.createServer(app)
server.keepAliveTimeout = 65000 // 65 seconds
// Ensure headers timeout is longer too
server.headersTimeout = 66000 // 66 seconds
```text
## Testing
### Test Keep-Alive
```bash
# Use telnet to send multiple requests
telnet example.com 80
GET / HTTP/1.1
Host: example.com
Connection: keep-alive
# Send another request on same connection
GET /page2 HTTP/1.1
Host: example.com
Connection: keep-alive
Test Connection Close
curl -v -H "Connection: close" https://api.example.com/data
# Look for:
# < Connection: close
# * Closing connection 0
```javascript
### Monitor Connection Pool
```javascript
// Node.js - monitor active connections
const server = http.createServer(app)
server.on('connection', (socket) => {
console.log('New connection')
socket.on('close', () => {
console.log('Connection closed')
})
})
Related Headers
- Keep-Alive - Keep-alive parameters
- Upgrade - Protocol upgrade
- Transfer-Encoding - Chunked encoding for persistent connections
- Content-Length - Required for connection reuse
HTTP/2 and the Connection Header
In HTTP/2, the Connection header is explicitly forbidden. HTTP/2 uses a single multiplexed connection for all requests to a server, and connection management is handled at the protocol level rather than through headers. If a client sends Connection: keep-alive in an HTTP/2 request, the server must treat it as a protocol error.
This distinction matters when building proxies or middleware that need to handle both HTTP/1.1 and HTTP/2. A proxy that forwards Connection headers from HTTP/1.1 clients to HTTP/2 upstream servers will cause protocol errors. The proxy must strip hop-by-hop headers (including Connection and any headers listed in the Connection header value) before forwarding requests upstream, regardless of the upstream protocol version.
Frequently Asked Questions
What is the Connection header?
Connection controls whether the network connection stays open after the current transaction. Values are keep-alive (reuse connection) or close (end connection).
Is Connection: keep-alive still needed?
In HTTP/1.1, keep-alive is the default so the header is optional. In HTTP/2 and HTTP/3, Connection is ignored as they handle connections differently.
When should I use Connection: close?
Use close when you know no more requests will follow, to free server resources. Servers may also send it when shutting down or under heavy load.
What is a hop-by-hop header?
Connection is hop-by-hop, meaning it applies only to the immediate connection, not end-to-end. Proxies must not forward Connection or headers it lists.