caddyserver/caddy · error
unsupported cipher suite: %s
Error message
unsupported cipher suite: %s
What it means
Returned by buildStandardTLSConfig when a ciphers/cipher_suites entry is not found in the supported list (CipherSuiteID returns 0). Caddy only accepts the IANA-style names it has registered; TLS 1.3 suites are not configurable in Go's TLS stack, and unknown or misspelled names fail here.
Source
Thrown at modules/caddytls/connpolicy.go:345
// session ticket key rotation
tlsApp.SessionTickets.register(cfg)
ctx.OnCancel(func() {
// do cleanup when the context is canceled because,
// though unlikely, it is possible that a context
// needing a TLS server config could exist for less
// than the lifetime of the whole app
tlsApp.SessionTickets.unregister(cfg)
})
}
// TODO: Clean up session ticket active locks in storage if app (or process) is being closed!
// add all the cipher suites in order, without duplicates
cipherSuitesAdded := make(map[uint16]struct{})
for _, csName := range p.CipherSuites {
csID := CipherSuiteID(csName)
if csID == 0 {
return fmt.Errorf("unsupported cipher suite: %s", csName)
}
if _, ok := cipherSuitesAdded[csID]; !ok {
cipherSuitesAdded[csID] = struct{}{}
cfg.CipherSuites = append(cfg.CipherSuites, csID)
}
}
// add all the curve preferences in order, without duplicates
curvesAdded := make(map[tls.CurveID]struct{})
for _, curveName := range p.Curves {
curveID := SupportedCurves[curveName]
if _, ok := curvesAdded[curveID]; !ok {
curvesAdded[curveID] = struct{}{}
cfg.CurvePreferences = append(cfg.CurvePreferences, curveID)
}
}
// ensure ALPN includes the ACME TLS-ALPN protocolView on GitHub (pinned to 50e54ee279)
Solutions
- Remove the offending name shown in the error
- Restrict cipher lists to TLS 1.2 suites from Go's supported set and drop TLS_AES_/TLS_CHACHA20_ names (1.3 suites are always-on)
- Re-copy a known-good list from Caddy's documentation
- Validate config with 'caddy validate' after editing
Example fix
# before
tls {
ciphers TLS_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
}
# after (TLS 1.3 suites are not configurable)
tls {
ciphers TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
} Defensive patterns
Strategy: validation
Validate before calling
// In Go, validate names against Caddy's registry before accepting user input:
func validCipher(name string) bool {
return caddytls.CipherSuiteID(name) != 0
} Prevention
- Copy cipher lists only from Caddy's own docs, not nginx/Mozilla generator output
- Never include TLS 1.3 suite names — Go enables them unconditionally
- Lint configs for cipher names against 'caddy list-modules'-adjacent docs in CI
When it happens
Trigger: A tls block listing a cipher name not in the supported map: misspellings, older GCM names, TLS 1.3 suite names (TLS_AES_128_GCM_SHA256 etc.), or names from other servers' docs. Applies per connection policy during Provision.
Common situations: Copying cipher lists from nginx/Apache/Mozilla SSL config generator output that includes names Caddy/Go do not support; hardening guides mixing TLS 1.2 and 1.3 suites; trailing whitespace or case differences.
Related errors
- invalid TLS renegotiation level: %v
- protocol min (%x) cannot be greater than protocol max (%x)
- could not convert automation policy subject '%s' to punycode
- unrecognized key type: %s
- no PEM keys specified
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/da9633a441c34f41.
Report an issue: GitHub.