caddyserver/caddy · error
two policies with same match criteria have conflicting cert
Error message
two policies with same match criteria have conflicting cert selections: %+v vs. %+v
What it means
Two connection policies with identical match criteria both define certificate selection rules (tls { cert_selection ... }) that differ in SerialNumber, SubjectOrganization, PublicKeyAlgorithm, or AllTags. Only AnyTag merging is implemented, so any other mismatch is fatal.
Source
Thrown at caddyconfig/httpcaddyfile/httptype.go:1305
if cps[i].ProtocolMin != "" &&
cps[j].ProtocolMin != "" &&
cps[i].ProtocolMin != cps[j].ProtocolMin {
return nil, fmt.Errorf("two policies with same match criteria have conflicting min protocol: %s vs. %s",
cps[i].ProtocolMin, cps[j].ProtocolMin)
}
if cps[i].ProtocolMax != "" &&
cps[j].ProtocolMax != "" &&
cps[i].ProtocolMax != cps[j].ProtocolMax {
return nil, fmt.Errorf("two policies with same match criteria have conflicting max protocol: %s vs. %s",
cps[i].ProtocolMax, cps[j].ProtocolMax)
}
if cps[i].CertSelection != nil && cps[j].CertSelection != nil {
// merging fields other than AnyTag is not implemented
if !reflect.DeepEqual(cps[i].CertSelection.SerialNumber, cps[j].CertSelection.SerialNumber) ||
!reflect.DeepEqual(cps[i].CertSelection.SubjectOrganization, cps[j].CertSelection.SubjectOrganization) ||
cps[i].CertSelection.PublicKeyAlgorithm != cps[j].CertSelection.PublicKeyAlgorithm ||
!reflect.DeepEqual(cps[i].CertSelection.AllTags, cps[j].CertSelection.AllTags) {
return nil, fmt.Errorf("two policies with same match criteria have conflicting cert selections: %+v vs. %+v",
cps[i].CertSelection, cps[j].CertSelection)
}
}
// by now we've decided that we can merge the two -- we'll keep i and drop j
if len(cps[i].ALPN) == 0 && len(cps[j].ALPN) > 0 {
cps[i].ALPN = cps[j].ALPN
}
if len(cps[i].CipherSuites) == 0 && len(cps[j].CipherSuites) > 0 {
cps[i].CipherSuites = cps[j].CipherSuites
}
if cps[i].ClientAuthentication == nil && cps[j].ClientAuthentication != nil {
cps[i].ClientAuthentication = cps[j].ClientAuthentication
}
if len(cps[i].Curves) == 0 && len(cps[j].Curves) > 0 {
cps[i].Curves = cps[j].Curves
}View on GitHub (pinned to 50e54ee279)
Solutions
- Keep a single cert_selection block per hostname set and delete duplicates
- If both are needed, make every field except AnyTag identical
- Move one selection to a distinct hostname so the SNI matchers differ
Example fix
# before
example.com {
tls {
cert_selection {
public_key_algorithm rsa
}
}
}
example.com {
tls {
cert_selection {
public_key_algorithm ecdsa
}
}
}
# after
example.com {
tls {
cert_selection {
public_key_algorithm rsa
}
}
} Defensive patterns
Strategy: validation
Validate before calling
for h, sels in group_certselection_by_host(sites).items():
assert len({canonical(s) for s in sels}) <= 1, f'conflicting cert_selection for {h}' Prevention
- One cert_selection per hostname
- Use tags (AnyTag) where possible — it is the only mergeable field
- Keep multi-cert domains on distinct subdomains per selection
When it happens
Trigger: Two site blocks for the same hostname(s) each declaring a `cert_selection` block with different criteria — e.g. one selects by serial number, another by public key algorithm — leaving both policies with the same SNI matchers at consolidation time.
Common situations: Choosing among multiple certs for the same name with duplicated site blocks, or importing a cert-selection snippet twice with tweaked values.
Related errors
- server listening on %v is HTTP, but attempts to configure TL
- two policies with same match criteria have conflicting ALPN:
- two policies with same match criteria have conflicting ciphe
- two policies with same match criteria have conflicting curve
- two policies with same match criteria have conflicting defau
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/fcf01a1f25524571.
Report an issue: GitHub.