caddyserver/caddy · error
no server TLS configuration available for ClientHello: %+v
Error message
no server TLS configuration available for ClientHello: %+v
What it means
Returned by the GetConfigForClient callback when a ClientHello arrived that no connection policy matched (and no default policy applied). The %+v payload dumps the whole ClientHelloInfo (server name, supported versions, ALPN, cipher suites, remote address), which is the key diagnostic: it shows exactly what the client asked for.
Source
Thrown at modules/caddytls/connpolicy.go:139
possiblePolicies := cp
if indexedPolicies, ok := indexedBySNI[asciiServerNameForMatch(hello.ServerName)]; ok {
possiblePolicies = indexedPolicies
}
policyLoop:
for _, pol := range possiblePolicies {
for _, matcher := range pol.matchers {
if !matcher.Match(hello) {
continue policyLoop
}
}
if pol.Drop {
return nil, fmt.Errorf("dropping connection")
}
return pol.TLSConfig, nil
}
return nil, fmt.Errorf("no server TLS configuration available for ClientHello: %+v", hello)
}
tlsCfg := &tls.Config{
MinVersion: tls.VersionTLS12,
GetConfigForClient: getConfigForClient,
}
// enable ECH, if configured
if tlsAppIface, err := ctx.AppIfConfigured("tls"); err == nil {
tlsApp := tlsAppIface.(*TLS)
if tlsApp.EncryptedClientHello != nil && len(tlsApp.EncryptedClientHello.configs) > 0 {
// if no publication was configured, we apply ECH to all server names by default,
// but the TLS app needs to know what they are in this case, since they don't appear
// in its config (remember, TLS connection policies are used by *other* apps to
// run TLS servers) -- we skip names with placeholders
if tlsApp.EncryptedClientHello.Publication == nil {
repl := caddy.NewReplacer()View on GitHub (pinned to 50e54ee279)
Solutions
- Look at the ServerName in the dumped ClientHello and compare it to the matchers (sni values) of each policy
- Add a policy (or site block) covering that name, or a final catch-all policy
- For SNI-less clients (IP connections), add a policy matching on no-SNI or set a default SNI
- For deep subdomains, add explicit names or restructure matching since *.example.com does not match a.b.example.com
Example fix
# before
example.com {
tls {
policies {
policy {
match { sni example.com }
}
}
}
}
# after (catch-all for anything else)
example.com {
tls {
policies {
policy {
match { sni example.com }
}
policy {
match { sni * }
}
}
}
} Defensive patterns
Strategy: validation
Validate before calling
# Enumerate SNIs your policies cover and diff against expected hostnames: caddy adapt --config Caddyfile --adapter caddyfile 2>/dev/null \ | grep -o '"sni"[^}]*}' | sort -u
Prevention
- Always include a final catch-all policy (or default site) if unknown SNIs should get a response instead of an error
- Remember single-level wildcards (*.example.com) do not match multi-level subdomains
- Log and review the ClientHello dump in the error — the ServerName field tells you exactly what to allow
When it happens
Trigger: ServerName-based policy index miss: the ClientHello's SNI is absent from all policies and no catch-all policy exists; client sends no SNI while all policies require sni matching; wildcards that do not cover the requested name (e.g. policy for *.example.com and client asks a.b.example.com).
Common situations: New domain pointed at the server but not added to the config; clients connecting by IP (empty SNI); a policy list tightened so the default fallback was removed; multi-level subdomain not covered by a single-level wildcard.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- dropping connection
- two policies with same match criteria have conflicting defau
- two policies with same match criteria have conflicting fallb
- loading handshake matchers: %v
- connection policy %d: building standard TLS config: %s
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/821f5ed5dd63df6b.
Report an issue: GitHub.