caddyserver/caddy · error
conflicting config for client authentication trust CA
Error message
conflicting config for client authentication trust CA
What it means
ClientAuthentication.provision rejects configurations that specify both the 'ca' module (CARaw, e.g. an internal PKI CA provider) and one of trusted_ca_certs / trusted_ca_certs_file for the same connection policy. Both are ways to define the client trust CA, and Caddy refuses to guess which one wins.
Source
Thrown at modules/caddytls/connpolicy.go:780
for block, rest := pem.Decode(certDataPEM); block != nil; block, rest = pem.Decode(rest) {
if block.Type != "CERTIFICATE" {
return nil, fmt.Errorf("no CERTIFICATE pem block found in %s", filename)
}
ders = append(
ders,
base64.StdEncoding.EncodeToString(block.Bytes),
)
}
// if we decoded nothing, return an error
if len(ders) == 0 {
return nil, fmt.Errorf("no CERTIFICATE pem block found in %s", filename)
}
return ders, nil
}
func (clientauth *ClientAuthentication) provision(ctx caddy.Context) error {
if len(clientauth.CARaw) > 0 && (len(clientauth.TrustedCACerts) > 0 || len(clientauth.TrustedCACertPEMFiles) > 0) {
return fmt.Errorf("conflicting config for client authentication trust CA")
}
// convert all named file paths to inline
if len(clientauth.TrustedCACertPEMFiles) > 0 {
for _, fpath := range clientauth.TrustedCACertPEMFiles {
ders, err := convertPEMFilesToDER(fpath)
if err != nil {
return err
}
clientauth.TrustedCACerts = append(clientauth.TrustedCACerts, ders...)
}
}
// if we have TrustedCACerts explicitly set, create an 'inline' CA and return
if len(clientauth.TrustedCACerts) > 0 {
caPool := InlineCAPool{
TrustedCACerts: clientauth.TrustedCACerts,
}View on GitHub (pinned to 50e54ee279)
Solutions
- Pick ONE trust source: either the 'ca' module or the static cert list/files, and remove the other
- If you merged two config fragments, diff your connection policy and delete the duplicate trust setting
- Validate the config before deploy: caddy validate --config Caddyfile
- If you genuinely need multiple trust anchors, put them all in one trust_ca bundle file and drop the 'ca' module
Example fix
# before
client_auth {
mode require_and_verify
ca internal
trusted_ca_cert_file /etc/caddy/ca.pem
}
# after
client_auth {
mode require_and_verify
trusted_ca_cert_file /etc/caddy/ca.pem
} Defensive patterns
Strategy: validation
Validate before calling
// Reject conflicting client-auth trust config before provisioning
type connPolicyLike struct {
ClientAuthentication *struct {
CA json.RawMessage `json:"ca,omitempty"`
TrustedCACerts []string `json:"trusted_ca_certs,omitempty"`
TrustedCACertPEMFiles []string `json:"trusted_ca_cert_pem_files,omitempty"`
}
}
func checkNoTrustConflict(p connPolicyLike) error {
ca := p.ClientAuthentication
if ca == nil {
return nil
}
if len(ca.CA) > 0 && (len(ca.TrustedCACerts) > 0 || len(ca.TrustedCACertPEMFiles) > 0) {
return fmt.Errorf("either 'ca' module or trusted_ca_certs(_pem_files), not both")
}
return nil
} Prevention
- Adopt one convention per fleet (module CA or static bundle) and lint configs for the other
- Treat 'caddy validate' as a required pre-deploy step
- Keep client_auth sections in a single shared snippet/include rather than duplicating them
When it happens
Trigger: In JSON: setting both "client_authentication": {"ca": {...}} and "trusted_ca_certs" or "trusted_ca_cert PEM files" on the same policy. In Caddyfile: mixing the 'ca' subdirective with 'trusted_ca_cert' / 'trusted_ca_cert_file' inside a client_auth block.
Common situations: Migrating from static CA pinning to the internal CA module (or vice versa) and leaving the old directive behind; copying example configs that use both styles; layered config includes that each add one option.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- client auth mode not recognized: %s
- two policies with same match criteria have conflicting clien
- client_certificate_file specified without client_certificate
- client_certificate_key_file specified without client_certifi
- could not convert automation policy subject '%s' to punycode
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/357696b14767be42.
Report an issue: GitHub.