caddyserver/caddy · error
two policies with same match criteria have conflicting fallb
Error message
two policies with same match criteria have conflicting fallback SNI: %s vs. %s
What it means
Two connection policies matching the same SNI criteria set different non-empty FallbackSNI values. FallbackSNI is used when the ClientHello SNI does not match any configured name; two conflicting fallbacks for the same policy scope cannot be merged.
Source
Thrown at caddyconfig/httpcaddyfile/httptype.go:1284
return nil, fmt.Errorf("two policies with same match criteria have conflicting client auth configuration: %+v vs. %+v",
cps[i].ClientAuthentication, cps[j].ClientAuthentication)
}
if len(cps[i].Curves) > 0 &&
len(cps[j].Curves) > 0 &&
!reflect.DeepEqual(cps[i].Curves, cps[j].Curves) {
return nil, fmt.Errorf("two policies with same match criteria have conflicting curves: %v vs. %v",
cps[i].Curves, cps[j].Curves)
}
if cps[i].DefaultSNI != "" &&
cps[j].DefaultSNI != "" &&
cps[i].DefaultSNI != cps[j].DefaultSNI {
return nil, fmt.Errorf("two policies with same match criteria have conflicting default SNI: %s vs. %s",
cps[i].DefaultSNI, cps[j].DefaultSNI)
}
if cps[i].FallbackSNI != "" &&
cps[j].FallbackSNI != "" &&
cps[i].FallbackSNI != cps[j].FallbackSNI {
return nil, fmt.Errorf("two policies with same match criteria have conflicting fallback SNI: %s vs. %s",
cps[i].FallbackSNI, cps[j].FallbackSNI)
}
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) ||View on GitHub (pinned to 50e54ee279)
Solutions
- Define fallback_sni once (global option) and remove per-site duplicates
- Make all fallback_sni values identical
- Isolate servers needing different fallbacks onto separate listeners
Example fix
# before
{
fallback_sni a.example.com
}
example.com {
tls {
# conflicting fallback set here via imported snippet: b.example.com
}
}
# after
{
fallback_sni a.example.com
}
example.com {
respond "ok"
} Defensive patterns
Strategy: validation
Validate before calling
assert count_of_fallback_sni_declarations(config) <= 1
Prevention
- Declare fallback_sni once, globally
- Audit snippets for stray fallback_sni when merging
- Keep default_sni/fallback_sni decisions in one reviewed file
When it happens
Trigger: `fallback_sni` declared in the global options and also (differently) via a tls policy for overlapping names on the same server, or two snippets each setting fallback_sni applied to the same site.
Common situations: Adding fallback_sni per-site after already defining it globally with a different value; importing shared snippets that both carry fallback_sni.
Related errors
- two policies with same match criteria have conflicting defau
- 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
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/45a380eeb0161dde.
Report an issue: GitHub.