HTTP Transport Tuning

Updated

September 8, 2026

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 maybe

HTTP/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

  1. Compare latency 100 serial gets with shared vs new client.
  2. Set MaxConnsPerHost: 2 and observe queuing.
  3. Forget Body.Close; watch FD/conn growth.