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 supported

View on GitHub (pinned to 50e54ee279)

Solutions

  1. 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
  2. Fix the filter values to match reality (correct tag name, current serial, right algorithm)
  3. Remove or widen overly strict filters (any_tag instead of all_tags) if intent is best-effort selection
  4. 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

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

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/7147b9a29cdd19b1. Report an issue: GitHub.