caddyserver/caddy · error
two policies with same match criteria have conflicting ALPN:
Error message
two policies with same match criteria have conflicting ALPN: %v vs. %v
What it means
Two TLS connection policies in the same server match exactly the same SNI criteria (MatchersRaw are DeepEqual) but declare different, non-empty ALPN protocol lists. Since both would apply to the same handshake and the adapter cannot pick one, adaptation fails instead of guessing.
Source
Thrown at caddyconfig/httpcaddyfile/httptype.go:1254
return nil, fmt.Errorf("recombining SNI matchers: %v", err)
}
cps = slices.Delete(cps, j, j+1)
i--
break
}
}
}
}
}
}
// if they have the same matcher, try to reconcile each field: either they must
// be identical, or we have to be able to combine them safely
if reflect.DeepEqual(cps[i].MatchersRaw, cps[j].MatchersRaw) {
if len(cps[i].ALPN) > 0 &&
len(cps[j].ALPN) > 0 &&
!reflect.DeepEqual(cps[i].ALPN, cps[j].ALPN) {
return nil, fmt.Errorf("two policies with same match criteria have conflicting ALPN: %v vs. %v",
cps[i].ALPN, cps[j].ALPN)
}
if len(cps[i].CipherSuites) > 0 &&
len(cps[j].CipherSuites) > 0 &&
!reflect.DeepEqual(cps[i].CipherSuites, cps[j].CipherSuites) {
return nil, fmt.Errorf("two policies with same match criteria have conflicting cipher suites: %v vs. %v",
cps[i].CipherSuites, cps[j].CipherSuites)
}
if cps[i].ClientAuthentication == nil &&
cps[j].ClientAuthentication != nil &&
!reflect.DeepEqual(cps[i].ClientAuthentication, cps[j].ClientAuthentication) {
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",View on GitHub (pinned to 50e54ee279)
Solutions
- Make the alpn lists identical in every block that shares those hostnames
- Delete the duplicate site block or tls block so only one policy exists
- Scope each site to a different port/server if they genuinely need different ALPN
Example fix
# before
example.com {
tls {
alpn h2
}
}
example.com {
tls {
alpn h2 http/1.1
}
}
# after
example.com {
tls {
alpn h2 http/1.1
}
} Defensive patterns
Strategy: validation
Validate before calling
# In config tooling: one alpn spec per hostname
seen = {}
for site in sites:
for h in site.hosts:
if site.alpn and h in seen and seen[h] != site.alpn:
raise ConfigError(f'conflicting alpn for {h}')
for h in site.hosts: seen.setdefault(h, site.alpn) Prevention
- Declare alpn once per hostname via a snippet
- Deduplicate site blocks in generated configs
- caddy adapt in CI catches these before deploy
When it happens
Trigger: Two site blocks with the same hostname(s) each setting `tls { alpn h2 }` vs `tls { alpn h2 http/1.1 }`, or a global policy and a site policy with differing alpn covering the same names, so after building the policy list both entries survive to consolidation with identical matchers.
Common situations: Duplicated site definitions (e.g. included snippet applied twice), one site in two config files imported together, or fine-tuning ALPN per site without realizing both blocks target the same server and names.
Related errors
- server listening on %v is HTTP, but attempts to configure TL
- 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
- two policies with same match criteria have conflicting fallb
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/c9a950bd29f51e68.
Report an issue: GitHub.