{"record":{"id":"c9a950bd29f51e68","repo":"caddyserver/caddy","slug":"two-policies-with-same-match-criteria-have-conflic","errorCode":null,"errorMessage":"two policies with same match criteria have conflicting ALPN: %v vs. %v","messagePattern":"two policies with same match criteria have conflicting ALPN: (.+?) vs\\. (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"caddyconfig/httpcaddyfile/httptype.go","lineNumber":1254,"sourceCode":"\t\t\t\t\t\t\t\t\t\treturn nil, fmt.Errorf(\"recombining SNI matchers: %v\", err)\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tcps = slices.Delete(cps, j, j+1)\n\t\t\t\t\t\t\t\t\ti--\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// if they have the same matcher, try to reconcile each field: either they must\n\t\t\t// be identical, or we have to be able to combine them safely\n\t\t\tif reflect.DeepEqual(cps[i].MatchersRaw, cps[j].MatchersRaw) {\n\t\t\t\tif len(cps[i].ALPN) > 0 &&\n\t\t\t\t\tlen(cps[j].ALPN) > 0 &&\n\t\t\t\t\t!reflect.DeepEqual(cps[i].ALPN, cps[j].ALPN) {\n\t\t\t\t\treturn nil, fmt.Errorf(\"two policies with same match criteria have conflicting ALPN: %v vs. %v\",\n\t\t\t\t\t\tcps[i].ALPN, cps[j].ALPN)\n\t\t\t\t}\n\t\t\t\tif len(cps[i].CipherSuites) > 0 &&\n\t\t\t\t\tlen(cps[j].CipherSuites) > 0 &&\n\t\t\t\t\t!reflect.DeepEqual(cps[i].CipherSuites, cps[j].CipherSuites) {\n\t\t\t\t\treturn nil, fmt.Errorf(\"two policies with same match criteria have conflicting cipher suites: %v vs. %v\",\n\t\t\t\t\t\tcps[i].CipherSuites, cps[j].CipherSuites)\n\t\t\t\t}\n\t\t\t\tif cps[i].ClientAuthentication == nil &&\n\t\t\t\t\tcps[j].ClientAuthentication != nil &&\n\t\t\t\t\t!reflect.DeepEqual(cps[i].ClientAuthentication, cps[j].ClientAuthentication) {\n\t\t\t\t\treturn nil, fmt.Errorf(\"two policies with same match criteria have conflicting client auth configuration: %+v vs. %+v\",\n\t\t\t\t\t\tcps[i].ClientAuthentication, cps[j].ClientAuthentication)\n\t\t\t\t}\n\t\t\t\tif len(cps[i].Curves) > 0 &&\n\t\t\t\t\tlen(cps[j].Curves) > 0 &&\n\t\t\t\t\t!reflect.DeepEqual(cps[i].Curves, cps[j].Curves) {\n\t\t\t\t\treturn nil, fmt.Errorf(\"two policies with same match criteria have conflicting curves: %v vs. %v\",","sourceCodeStart":1236,"sourceCodeEnd":1272,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddyconfig/httpcaddyfile/httptype.go#L1236-L1272","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nexample.com {\n  tls {\n    alpn h2\n  }\n}\nexample.com {\n  tls {\n    alpn h2 http/1.1\n  }\n}\n# after\nexample.com {\n  tls {\n    alpn h2 http/1.1\n  }\n}","handlingStrategy":"validation","validationCode":"# In config tooling: one alpn spec per hostname\nseen = {}\nfor site in sites:\n    for h in site.hosts:\n        if site.alpn and h in seen and seen[h] != site.alpn:\n            raise ConfigError(f'conflicting alpn for {h}')\n    for h in site.hosts: seen.setdefault(h, site.alpn)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Declare alpn once per hostname via a snippet","Deduplicate site blocks in generated configs","caddy adapt in CI catches these before deploy"],"tags":["caddy","tls","alpn","caddyfile","conflict"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}