go 28 lines · 1 tab

HTTP client tuned for production: timeouts, transport, and connection reuse

Leah Thompson Jan 2026
1 tab
package deps

import (
  "net"
  "net/http"
  "time"
)

func NewHTTPClient() *http.Client {
  tr := &http.Transport{
    Proxy: http.ProxyFromEnvironment,
    DialContext: (&net.Dialer{
      Timeout:   3 * time.Second,
      KeepAlive: 30 * time.Second,
    }).DialContext,
    ForceAttemptHTTP2:     true,
    MaxIdleConns:          200,
    MaxIdleConnsPerHost:   50,
    IdleConnTimeout:       90 * time.Second,
    TLSHandshakeTimeout:   3 * time.Second,
    ExpectContinueTimeout: 1 * time.Second,
  }

  return &http.Client{
    Timeout:   5 * time.Second,
    Transport: tr,
  }
}
1 file · go Explain with highlit

The default http.Client is deceptively easy to misuse. I always set a request timeout (either via client.Timeout for simple cases or context.WithTimeout per request) and I tune the Transport so we reuse connections aggressively without leaking idle sockets forever. MaxIdleConnsPerHost is a common bottleneck for high fan-out services, and TLSHandshakeTimeout prevents handshake stalls from pinning goroutines. For services that call other services, these settings reduce tail latency and help avoid cascading failures. The biggest win is consistency: if every outbound call has a timeout, your service won’t hang just because a dependency is slow. Pair this with retries and you get predictable behavior.


Related snips

Share this code

Here's the card — post it anywhere.

HTTP client tuned for production: timeouts, transport, and connection reuse — share card
Link copied