HTTP Transport Tuning
HTTP Transport Tuning
Overview
http.Transport owns connection pooling, TLS, proxies, and HTTP/2. Defaults are fine for many apps; services with high fan-out need explicit limits.
Sensible client
func NewClient() *http.Client {
t := &http.Transport{
Proxy: http.ProxyFromEnvironment,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
MaxConnsPerHost: 0, // 0 = unlimited; set in tight systems
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 5 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
DialContext: (&net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
}
return &http.Client{Transport: t, Timeout: 15 * time.Second}
}One client per process
Reuse *http.Client. Creating per-request clients throws away the pool.
Timeouts layers
Client.Timeout → entire request including body
Dialer.Timeout → TCP connect
TLSHandshakeTimeout → TLS
Response header → via context on request
Prefer NewRequestWithContext + outer timeout.
Disable keep-alives (rare)
t.DisableKeepAlives = true // short CLI one-shots maybeHTTP/2
Enabled automatically for HTTPS with default transport settings. HTTP/2 multiplexes streams on one conn—still bound total concurrency at app layer.
Rules of thumb
| Do | Don’t |
|---|---|
| Share Transport | http.Get without timeout forever |
| Cap per-host conns under load | Unlimited fan-out to one dependency |
| Close response bodies | Leak connections (unread body) |
Try next
- Compare latency 100 serial gets with shared vs new client.
- Set
MaxConnsPerHost: 2and observe queuing.
- Forget
Body.Close; watch FD/conn growth.