caddyserver/caddy · error
no certificates matched custom selection policy
Error message
no certificates matched custom selection policy
What it means
Returned by CustomCertSelectionPolicy.SelectCertificate when at least one certificate was loaded but none satisfied every configured filter (tags, serial numbers, key algorithm, etc.). This is a certificate-choice failure inside certmagic after viable filtering, distinct from 'no certs loaded at all'.
Source
Thrown at modules/caddytls/certselection.go:110
continue
}
}
if len(p.AllTags) > 0 {
for _, tag := range p.AllTags {
if !cert.HasTag(tag) {
continue nextChoice
}
}
}
// this certificate at least meets the policy's requirements,
// but we still have to check expiration and compatibility
viable = append(viable, cert)
}
if len(viable) == 0 {
return certmagic.Certificate{}, fmt.Errorf("no certificates matched custom selection policy")
}
return certmagic.DefaultCertificateSelector(hello, viable)
}
// UnmarshalCaddyfile sets up the CustomCertSelectionPolicy from Caddyfile tokens. Syntax:
//
// cert_selection {
// all_tags <values...>
// any_tag <values...>
// public_key_algorithm <dsa|ecdsa|rsa>
// serial_number <big_integers...>
// subject_organization <values...>
// }
func (p *CustomCertSelectionPolicy) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
_, wrapper := d.Next(), d.Val() // consume wrapper name
// No same-line options are supportedView on GitHub (pinned to 50e54ee279)
Solutions
- List the loaded certificates and their tags/serials/alggorithms (e.g. inspect storage or 'caddy list-modules'-adjacent admin endpoints) and compare against the cert_selection filters
- Fix the filter values to match reality (correct tag name, current serial, right algorithm)
- Remove or widen overly strict filters (any_tag instead of all_tags) if intent is best-effort selection
- If certificates were meant to be tagged, re-tag them at load/issuance time and reload
Example fix
# before
cert_selection {
any_tag staging
}
# after (certificates in storage are tagged "prod")
cert_selection {
any_tag prod
} Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: assert at least one loaded cert satisfies the tag filter
func anyCertMatches(certs []certmagic.Certificate, tags []string) bool {
for _, c := range certs {
for _, want := range tags {
if c.HasTag(want) {
return true
}
}
}
return false
} Prevention
- Define tag names once (in a shared variable/pipeline) and use them for both tagging and cert_selection
- Prefer any_tag over all_tags unless multiple tags are genuinely required
- Avoid pinning serial numbers in long-lived configs since reissuance changes them
- After reissuing or re-tagging certs, reload Caddy and smoke-test one handshake per policy
When it happens
Trigger: A cert_selection block with all_tags/any_tag/serial_number/public_key_algorithm filters that match zero of the loaded certificates; tags on certificates were renamed or removed in storage while the policy still references old names; an issuance renewal changed the serial number the policy pins.
Common situations: Operator configures cert_selection { any_tag prod } but certificates in storage were tagged differently (or not tagged); policy pinned a serial number and the cert was re-issued, changing it; ECDSA-only policy after migrating to RSA certs.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- URL is required
- two policies with same match criteria have conflicting cert
- finalizing automatic HTTPS: %v
- loading external certificate manager modules: %v
- parsing certificate at index %d: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/7147b9a29cdd19b1.
Report an issue: GitHub.