caddyserver/caddy · error

two policies with same match criteria have conflicting ciphe

Error message

two policies with same match criteria have conflicting cipher suites: %v vs. %v

What it means

Two TLS connection policies with identical SNI match criteria specify different non-empty cipher suite lists. The adapter will not silently choose one cipher list for a handshake both policies govern, so it aborts with the two lists printed for comparison.

Source

Thrown at caddyconfig/httpcaddyfile/httptype.go:1260

							}
						}
					}
				}
			}

			// 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",
						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",

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Use one identical `ciphers` list everywhere the hostname appears
  2. Remove ciphers entirely to accept Caddy defaults (recommended)
  3. Delete or deduplicate the redundant site block
  4. Put the conflicting sites on separate servers (distinct ports/binds)

Example fix

# before
example.com {
  tls {
    ciphers TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  }
}
example.com {
  tls {
    ciphers TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  }
}
# after
example.com {
  tls {
    ciphers TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  }
}
Defensive patterns

Strategy: validation

Validate before calling

# One ciphers list per hostname across all blocks
for h, lists in group_ciphers_by_host(sites).items():
    assert len({tuple(l) for l in lists if l}) <= 1, f'conflicting ciphers for {h}'

Prevention

When it happens

Trigger: Same-hostname site blocks with `tls { ciphers ... }` listing different suites (e.g. one modern list, one including TLS_RSA suites), or a site-level ciphers setting conflicting with a snippet/global tls policy applied to the same names on the same server.

Common situations: Copy-pasted legacy cipher lists pasted into two blocks for the same domain, snippets imported into multiple site blocks with per-import cipher overrides, or migrating cipher config between versions leaving a stale duplicate block.

Related errors


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