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

  1. Make the alpn lists identical in every block that shares those hostnames
  2. Delete the duplicate site block or tls block so only one policy exists
  3. 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

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


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