caddyserver/caddy · error
making TLS client config: %v
Error message
making TLS client config: %v
What it means
The reverse_proxy HTTP transport delegates to the TLS connection policy module (`h.TLS.MakeTLSClientConfig`) to build a *tls.Config for upstream connections. Any failure there — bad client certificate files, invalid cipher suites, unsupported TLS version bounds — surfaces wrapped with this message during provisioning.
Source
Thrown at modules/caddyhttp/reverseproxy/httptransport.go:417
}
rt := &http.Transport{
Proxy: proxyWrapper,
DialContext: dialContext,
MaxConnsPerHost: h.MaxConnsPerHost,
ResponseHeaderTimeout: time.Duration(h.ResponseHeaderTimeout),
ExpectContinueTimeout: time.Duration(h.ExpectContinueTimeout),
MaxResponseHeaderBytes: h.MaxResponseHeaderSize,
WriteBufferSize: h.WriteBufferSize,
ReadBufferSize: h.ReadBufferSize,
}
if h.TLS != nil {
rt.TLSHandshakeTimeout = time.Duration(h.TLS.HandshakeTimeout)
var err error
rt.TLSClientConfig, err = h.TLS.MakeTLSClientConfig(caddyCtx)
if err != nil {
return nil, fmt.Errorf("making TLS client config: %v", err)
}
serverNameHasPlaceholder := strings.Contains(h.TLS.ServerName, "{")
// We need to use custom DialTLSContext if:
// 1. ServerName has a placeholder that needs to be replaced at request-time, OR
// 2. ProxyProtocol is enabled, because req.URL.Host is modified to include
// client address info with "->" separator which breaks Go's address parsing
if serverNameHasPlaceholder || h.ProxyProtocol != "" {
rt.DialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
// reuses the dialer from above to establish a plaintext connection
conn, err := dialContext(ctx, network, addr)
if err != nil {
return nil, err
}
// but add our own handshake logic
tlsConfig := rt.TLSClientConfig.Clone()View on GitHub (pinned to 50e54ee279)
Solutions
- Read the nested cause after the colon — it names the exact TLS setting that failed
- Verify file paths are absolute or relative to where caddy runs, and that cert/key are valid PEM
- Restrict tls_versions to tls1.2/tls1.3 and remove cipher overrides unless required (Go 1.3 suites are fixed)
- Validate the pair with openssl: openssl x509 -in cert.pem -noout && openssl pkey -in key.pem -noout
Example fix
# before
transport http {
tls {
ciphers TLS_ECDHE_RSA_WITH_AES_128_SHA256
}
}
# after
transport http {
tls
} Defensive patterns
Strategy: validation
Validate before calling
// preflight the key material used by the TLS block
func checkTLSFiles(certPath, keyPath, caPath string) error {
for _, p := range []string{certPath, keyPath, caPath} {
if p == "" {
continue
}
if _, err := os.Stat(p); err != nil {
return fmt.Errorf("tls file %s: %w", p, err)
}
}
return nil
} Prevention
- Use absolute paths for cert/CA files in proxy TLS blocks
- Validate PEMs with openssl during mTLS rotation before reloading Caddy
- Avoid cipher/version overrides unless a specific upstream requires them
When it happens
Trigger: `transport http { tls_client_auth { cert_file ... key_file ... } }` pointing to unreadable/corrupt PEM files; `tls_versions` with values like `tls1.4`; `ciphers` naming an unsupported suite; bad `tls_trusted_ca_certs` file in reverse_proxy's tls block.
Common situations: Cert paths relative to the wrong working directory, PEM files containing a cert where a key is expected, mTLS rotation leaving a half-updated keypair, or copying TLS settings from an nginx frontend config with different cipher names.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- unsupported HTTP version: %s, supported version: %s
- failed to load network_proxy module: %v
- making TLS client config for HTTP/3 transport: %v
- consolidating TLS connection policies for server %d: %v
- it is unnecessary to specify the TLS listener wrapper in the
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/98da3b14c5189fda.
Report an issue: GitHub.