caddyserver/caddy · error

two policies with same match criteria have conflicting curve

Error message

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

What it means

Two TLS connection policies with the same match criteria declare different non-empty elliptic curve lists (`tls { curves ... }`). Caddy cannot merge two distinct curve preferences for connections both policies would govern, so adaptation stops and prints both lists.

Source

Thrown at caddyconfig/httpcaddyfile/httptype.go:1272

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

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Use the same curves list (or omit curves for secure defaults) in all blocks for those hostnames
  2. Deduplicate the site block
  3. Split genuinely different curve needs across different servers/ports

Example fix

# before
example.com {
  tls {
    curves x25519
  }
}
example.com {
  tls {
    curves secp256r1
  }
}
# after
example.com {
  tls {
    curves x25519 secp256r1
  }
}
Defensive patterns

Strategy: validation

Validate before calling

for h, lists in group_curves_by_host(sites).items():
    assert len({tuple(l) for l in lists if l}) <= 1, f'conflicting curves for {h}'

Prevention

When it happens

Trigger: Site blocks sharing hostnames where one sets `tls { curves x25519 }` and another sets `tls { curves secp256r1 x25519 }` on the same server, surviving consolidation with identical SNI matchers.

Common situations: Tuning curves for old-client compatibility on one copy of a duplicated site block, or applying a curves snippet inconsistently across imports of the same domain.

Related errors


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