caddyserver/caddy · error
automation policy from site block is also default/catch-all
Error message
automation policy from site block is also default/catch-all policy because of key without hostname, and the two are in conflict: %#v != %#v
What it means
When a site block's address has no hostname (e.g. ':443'), its automation policy doubles as the catch-all policy for all sites. If another such site block already established a different issuer list (ap.Issuers vs issuers), the two catch-all policies conflict and adaptation aborts rather than silently picking one. The source comment shows the canonical repro: two ':443' blocks, one with 'tls { issuer acme }' and one without.
Source
Thrown at caddyconfig/httpcaddyfile/tlsapp.go:172
var issuers []certmagic.Issuer
for _, issuerVal := range issuerVals {
issuers = append(issuers, issuerVal.Value.(certmagic.Issuer))
}
if ap == catchAllAP && !reflect.DeepEqual(ap.Issuers, issuers) {
// this more correctly implements an error check that was removed
// below; try it with this config:
//
// :443 {
// bind 127.0.0.1
// }
//
// :443 {
// bind ::1
// tls {
// issuer acme
// }
// }
return nil, warnings, fmt.Errorf("automation policy from site block is also default/catch-all policy because of key without hostname, and the two are in conflict: %#v != %#v", ap.Issuers, issuers)
}
ap.Issuers = issuers
}
// certificate managers
if certManagerVals, ok := sblock.pile["tls.cert_manager"]; ok {
for _, certManager := range certManagerVals {
certGetterName := certManager.Value.(caddy.Module).CaddyModule().ID.Name()
ap.ManagersRaw = append(ap.ManagersRaw, caddyconfig.JSONModuleObject(certManager.Value, "via", certGetterName, &warnings))
}
}
// custom bind host
for _, cfgVal := range sblock.pile["bind"] {
for _, iss := range ap.Issuers {
// if an issuer was already configured and it is NOT an ACME issuer,
// skip, since we intend to adjust only ACME issuers; ensure we
// include any issuer that embeds/wraps an underlying ACME issuer
var acmeIssuer *caddytls.ACMEIssuerView on GitHub (pinned to 50e54ee279)
Solutions
- Give at least one block a hostname so its policy is no longer catch-all (e.g. 'example.com:443 { ... }'), as the source comment implies.
- Make the issuer configuration identical across the conflicting catch-all blocks, or move issuer config to one place only.
- Use distinct ports for the two catch-all sites so they do not merge into one policy scope.
Example fix
# before
:443 {
bind 127.0.0.1
}
:443 {
bind ::1
tls {
issuer acme
}
}
# after
localhost:443 {
bind 127.0.0.1
}
[::1]:443 {
bind ::1
tls {
issuer acme
}
} Defensive patterns
Strategy: validation
Prevention
- Avoid multiple hostname-less (catch-all) site blocks with differing tls settings.
- Put catch-all TLS issuer configuration in one place only (global or one block).
- Validate with 'caddy validate' before reload.
When it happens
Trigger: Two site blocks keyed by hostname-less addresses (':443', 'https://:443') where global issuers were captured first and the second block's site-level tls issuers differ — e.g. one block sets 'tls { issuer acme }' (or internal) and the other leaves defaults.
Common situations: Splitting a config into ':443' blocks bound to different interfaces with distinct TLS settings; adding a new catch-all site with different issuer config next to an existing one.
Related errors
- hostname appears in more than one automation policy, making
- consolidating TLS connection policies for server %d: %v
- server listening on %v is HTTP, but attempts to configure TL
- two policies with same match criteria have conflicting ALPN:
- two policies with same match criteria have conflicting ciphe
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/b40ce2241afe54a3.
Report an issue: GitHub.