caddyserver/caddy · error
no client certificate provided
Error message
no client certificate provided
What it means
LeafCertClientAuth.VerifyClientCertificate is the callback Go TLS invokes with the peer's raw certificate chain; if the slice is empty (no certificate was presented) it fails with this error. In practice it fires when the TLS mode let the handshake reach verification without a client cert — e.g. mode request combined with a leaf verifier, or a client that sent an empty Certificate message.
Source
Thrown at modules/caddytls/connpolicy.go:1033
mod, err := caddyfile.UnmarshalModule(d, "tls.leaf_cert_loader."+modName)
if err != nil {
return d.WrapErr(err)
}
vMod, ok := mod.(LeafCertificateLoader)
if !ok {
return fmt.Errorf("leaf module '%s' is not a leaf certificate loader", vMod)
}
l.LeafCertificateLoadersRaw = append(
l.LeafCertificateLoadersRaw,
caddyconfig.JSONModuleObject(vMod, "loader", modName, nil),
)
}
return nil
}
func (l LeafCertClientAuth) VerifyClientCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error {
if len(rawCerts) == 0 {
return fmt.Errorf("no client certificate provided")
}
remoteLeafCert, err := x509.ParseCertificate(rawCerts[0])
if err != nil {
return fmt.Errorf("can't parse the given certificate: %s", err.Error())
}
if slices.ContainsFunc(l.trustedLeafCerts, remoteLeafCert.Equal) {
return nil
}
return fmt.Errorf("client leaf certificate failed validation")
}
// PublicKeyAlgorithm is a JSON-unmarshalable wrapper type.
type PublicKeyAlgorithm x509.PublicKeyAlgorithm
// UnmarshalJSON satisfies json.Unmarshaler.View on GitHub (pinned to 50e54ee279)
Solutions
- Send a client certificate: curl --cert client.pem --key client.key https://host/
- Align the mode with your intent: use require_and_verify so missing certs are rejected earlier and clearly
- Confirm the client actually loads its cert (check client-side logs; many tools warn when a cert file fails to parse)
- If some clients legitimately have no cert, use verify_if_given and ensure your verifier tolerates that path per current Caddy semantics
Example fix
# before curl https://mtls.example.com/ # after curl --cert client.pem --key client.key https://mtls.example.com/
Defensive patterns
Strategy: validation
Validate before calling
// Client-side: verify you actually have a cert+key pair before connecting
func canPresentCert(certFile, keyFile string) error {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return fmt.Errorf("client cert unusable: %w", err)
}
_ = cert
return nil
} Try / catch
// Server-side logging: distinguish missing vs untrusted client certs
if err := conn.VerifyConnection(...); err != nil { /* caddy logs it */ }
// In access logs / metrics, alert on TLS handshake failures per client IP to catch cert-less clients Prevention
- Use mode require_and_verify for strict mTLS so empty-certificate clients are rejected with standard alerts
- Automate client cert provisioning so clients never connect without one
- Monitor handshake failure rates as an operational signal
When it happens
Trigger: client_auth mode 'request' (or verify_if_given) with a leaf verifier: the client sends no certificate and Go still calls the custom verifier path in some configurations; a client connecting without any cert configured when verification is active.
Common situations: Testing mTLS endpoints with plain curl (no --cert) while a leaf verifier is configured; clients with a cert that fails to load and silently send none; policy mismatch between mode and verifiers.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- can't parse the given certificate: %s
- client leaf certificate failed validation
- two policies with same match criteria have conflicting clien
- client_certificate_file specified without client_certificate
- client_certificate_key_file specified without client_certifi
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/135d7f1a69525fbe.
Report an issue: GitHub.