When testing HTTP keep-alive functionality from the client side, we need to examine both the connection behavior and the actual HTTP headers. Here are several professional approaches:
Modern browsers provide network inspection capabilities:
// Chrome DevTools example:
1. Open DevTools (F12)
2. Navigate to Network tab
3. Enable "Preserve log"
4. Look for "Connection: keep-alive" in response headers
5. Check if multiple requests reuse the same connection ID
cURL provides detailed connection information:
curl -Iv http://example.com --http1.1 2>&1 | grep -i "keep-alive\|connection"
// Alternative verbose version:
curl -v http://example.com --http1.1 -o /dev/null
// Look for "Re-using existing connection" in output
For deep inspection of TCP connections:
1. Capture network traffic with Wireshark
2. Filter with: tcp.port == 80 || tcp.port == 443
3. Check TCP handshake (SYN/SYN-ACK/ACK)
4. Subsequent requests should reuse same connection
5. Look for FIN packets indicating connection closure
Programmatic verification with connection pooling:
import requests
from requests.adapters import HTTPAdapter
session = requests.Session()
adapter = HTTPAdapter(pool_connections=1, pool_maxsize=1)
session.mount('http://', adapter)
# First request establishes connection
response1 = session.get('http://example.com')
print("Connection:", response1.headers.get('Connection'))
# Second request should reuse connection
response2 = session.get('http://example.com/page2')
print("Reused connection:", response2.raw._connection)
# Verify with netstat:
# netstat -an | grep ESTABLISHED | grep 80
Professional utilities for connection analysis:
# Using httptrace
httptrace -H "Connection: keep-alive" http://example.com
# Using Apache Benchmark (ab)
ab -k -c 10 -n 100 http://example.com/
# The -k flag enables keep-alive
When troubleshooting keep-alive issues, check:
- Server configuration (Apache KeepAliveTimeout, Nginx keepalive_timeout)
- Intermediate proxies that might close connections
- Client-side connection pool settings
- HTTP/1.1 vs HTTP/2 behavior differences
HTTP/2 always uses persistent connections:
curl --http2 -v https://example.com
# Look for "Using HTTP2, server supports multi-use"
The most definitive way to verify keep-alive is packet-level inspection. Capture traffic while making multiple requests:
1. Start Wireshark capture
2. Filter with: tcp.port == 80 || tcp.port == 443
3. Make consecutive requests to target server
4. Check TCP handshake occurrences:
- Single handshake for multiple requests = keep-alive working
- Multiple handshakes = keep-alive failing
Use CURL's verbose mode to inspect connection reuse:
curl -Iv --http1.1 https://example.com
curl -Iv --http1.1 https://example.com/second-page
Key indicators in output:
* Connection #0 to host example.com left intact
* Re-using existing connection! (#0) with host example.com
Modern browsers provide network connection inspection:
- Open DevTools (F12) → Network tab
- Enable "Preserve log"
- Make multiple requests
- Check "Connection ID" column (Chrome/Edge)
- Same ID = connection reused
Programmatic verification with connection pooling:
import requests
from requests.adapters import HTTPAdapter
session = requests.Session()
adapter = HTTPAdapter(pool_connections=1, pool_maxsize=1)
session.mount('http://', adapter)
# First request
response1 = session.get('http://example.com')
print(f"Initial connection: {response1.raw._connection}")
# Second request
response2 = session.get('http://example.com/about')
print(f"Reused connection: {response2.raw._connection}")
Verify keep-alive headers in server responses:
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: timeout=5, max=100
Test using telnet:
telnet example.com 80
GET / HTTP/1.1
Host: example.com
Connection: keep-alive
Benchmark difference with/without keep-alive:
ab -n 1000 -c 10 -k http://example.com/ # With keep-alive
ab -n 1000 -c 10 http://example.com/ # Without keep-alive
Compare "Requests per second" metrics - keep-alive should show better performance.
Test connection reuse programmatically:
const http = require('http');
const agent = new http.Agent({ keepAlive: true });
function makeRequest(path) {
return new Promise((resolve) => {
const req = http.request({
host: 'example.com',
port: 80,
path: path,
agent: agent
}, (res) => {
resolve(res.socket._handle.fd);
});
req.end();
});
}
(async () => {
const fd1 = await makeRequest('/');
const fd2 = await makeRequest('/contact');
console.log(Same socket? ${fd1 === fd2});
})();