Skip to content

Defaults and limits

Every value NewClient() and NewTransport() set when you pass no options, and every setting they deliberately leave alone. The options that change them are documented in client options.

What NewClient sets on the *http.Client

Field Value Changed by
Timeout 30s WithTimeout
Transport the transport from NewTransport, wrapped by retry and the middleware chain when configured WithTransport, WithRetry, WithClientMiddleware
CheckRedirect policy capping the chain at 10 requests, refusing an HTTPS→HTTP downgrade, and stripping named sensitive headers cross-origin WithMaxRedirects, WithSensitiveHeaders
Jar not set — the client stores no cookies nothing; assign one yourself

Each call to NewClient builds its own transport and therefore its own connection pool. Build one client per downstream service and reuse it; a client created per request gets no connection reuse and leaks idle connections until they time out.

What NewTransport sets on the *http.Transport

Field Value Bounds
Proxy http.ProxyFromEnvironment proxy selection from the environment
TLSClientConfig DefaultConfig() from go/tls, or the config you pass see TLS settings
MaxIdleConns 100 idle connections kept across all hosts
MaxIdleConnsPerHost 10 idle connections kept per host
IdleConnTimeout 90s how long an idle connection is kept before closing
TLSHandshakeTimeout 10s handshake must complete within this
ExpectContinueTimeout 1s wait for a 100 Continue after an Expect header
ResponseHeaderTimeout 30s time from finishing the request to the first response byte
DialContext net.Dialer{Timeout: 30s, KeepAlive: 30s} TCP connect timeout and keep-alive interval

ResponseHeaderTimeout is the one to reach for when a downstream hangs after accepting the request: it fails the attempt without waiting for the whole 30-second client timeout, and it does not cover the response body, so a slow large download is not cut short by it.

There is no option that changes an individual transport field. Build a transport with NewTransport, adjust the field, and pass it to WithTransport.

Which transport settings are left at Go's zero value

These are not configured, so the standard library's behaviour applies unchanged. They are listed because "is there a limit on X?" is usually answered here.

Field Zero value means
MaxConnsPerHost unlimited total connections per host — only idle connections are capped
ForceAttemptHTTP2 HTTP/2 is not attempted; see HTTP/2 is not negotiated
DisableKeepAlives keep-alives are on
DisableCompression Accept-Encoding: gzip is added and responses are transparently decompressed
MaxResponseHeaderBytes Go's conservative default of 10 MiB of response headers
ReadBufferSize / WriteBufferSize Go's 4 KiB buffers
TLSNextProto left nil, so nothing blocks HTTP/2 from being wired up — but nothing wires it up either, because a custom DialContext and TLSClientConfig are set and ForceAttemptHTTP2 is false

Nothing here bounds the size of a response body. A hostile or broken server can stream until the client timeout expires; wrap the body in an io.LimitReader if that matters.

The TLS settings that come from go/tls

DefaultConfig() in go/tls supplies:

Setting Value
MinVersion TLS 1.2
MaxVersion not set — TLS 1.3 is used when the server offers it
CipherSuites ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, ECDHE_RSA_WITH_AES_256_GCM_SHA384, ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, ECDHE_RSA_WITH_AES_128_GCM_SHA256, ECDHE_ECDSA_WITH_CHACHA20_POLY1305, ECDHE_RSA_WITH_CHACHA20_POLY1305
CurvePreferences X25519, P-256
RootCAs not set — the host's system trust store
InsecureSkipVerify false; certificate verification is always on

The cipher-suite list applies to TLS 1.0–1.2 only. TLS 1.3 cipher suites are not configurable in Go, so a TLS 1.3 connection uses Go's own suites regardless of this list.

Which environment variables the client reads

Only the standard proxy variables, and only because NewTransport sets Proxy: http.ProxyFromEnvironment:

Variable Effect
HTTP_PROXY / http_proxy proxy for http:// requests
HTTPS_PROXY / https_proxy proxy for https:// requests
NO_PROXY / no_proxy comma-separated hosts, domains or CIDRs to reach directly

The value may be a full URL or a host:port, in which case http is assumed. Requests to localhost or a loopback address never use a proxy. net/http reads these variables once per process and caches the result, so changing them after the first request has no effect.

WithTransport replaces the transport and therefore drops proxy support unless the transport you supply sets Proxy itself.

This module reads no other environment variables, and it has no configuration file and no configuration keys. Everything else is a code option.

What DefaultRetryConfig gives you

transithttp.DefaultRetryConfig() from go/transit, passed to WithRetry:

Field Default Notes
MaxRetries 3 four attempts in total
InitialBackoff 500ms first backoff, before jitter
MaxBackoff 30s caps the backoff and any Retry-After
RetryableStatusCodes 429, 502, 503, 504 a nil slice adopts this list; an empty slice means network errors only
RetryAllMethods false only GET, HEAD, OPTIONS, PUT and DELETE are retried
RetryableMethods nil overrides the idempotent set when supplied
ShouldRetry nil replaces the built-in decision entirely when supplied

The delay is full jitter — a uniform random draw between zero and the exponential bound — so backoff is an upper limit, not a fixed wait. A Retry-After header takes precedence and is clamped to MaxBackoff.

Retry is off unless WithRetry is passed. Note that the total wall-clock cost of a retrying request can exceed the 30-second default client timeout; see WithTimeout.

What DefaultCircuitBreakerConfig gives you

transithttp.DefaultCircuitBreakerConfig(), for use inside a ClientChain:

Field Default Notes
FailureThreshold 5 consecutive failures that trip the breaker open
Cooldown 30s how long it stays open before a trial request
HalfOpenMaxRequests 1 trial requests allowed; the first success closes it
IsFailure nil defaults to transport errors and status ≥ 500

A 429 does not count as a failure — rate limiting is retry's concern, not a signal that the downstream is unhealthy. While the breaker is open, calls fail immediately with transithttp.ErrCircuitOpen, testable with errors.Is.

WithRateLimit(rps) in the same chain is a token bucket with a burst of 1, shared by every request through that transport; it blocks until a token is free or the request context is cancelled.