caddyserver/caddy · error
%s: route %d, matcher set %d, matcher %d, host matcher %d: %
Error message
%s: route %d, matcher set %d, matcher %d, host matcher %d: %v
What it means
Thrown during automatic HTTPS provisioning when a host matcher value in a server's route cannot have placeholders resolved. The HTTP app walks every route's matcher sets, finds MatchHost entries, and calls replacer.ReplaceOrErr(d, true, false); if a placeholder inside a host name errors (e.g. an environment variable placeholder that is unknown or empty with error-on-empty semantics), provisioning aborts with the server name and precise indexes of the offending matcher.
Source
Thrown at modules/caddyhttp/autohttps.go:158
logger.Info("server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS",
zap.String("server_name", srvName),
zap.Int("https_port", app.httpsPort()),
)
srv.TLSConnPolicies = caddytls.ConnectionPolicies{new(caddytls.ConnectionPolicy)}
}
// find all qualifying domain names (deduplicated) in this server
// (this is where we need the provisioned, decoded request matchers)
serverDomainSet := make(map[string]struct{})
for routeIdx, route := range srv.Routes {
for matcherSetIdx, matcherSet := range route.MatcherSets {
for matcherIdx, m := range matcherSet {
if hm, ok := m.(*MatchHost); ok {
for hostMatcherIdx, d := range *hm {
var err error
d, err = repl.ReplaceOrErr(d, true, false)
if err != nil {
return fmt.Errorf("%s: route %d, matcher set %d, matcher %d, host matcher %d: %v",
srvName, routeIdx, matcherSetIdx, matcherIdx, hostMatcherIdx, err)
}
if !slices.Contains(srv.AutoHTTPS.Skip, d) {
serverDomainSet[d] = struct{}{}
}
}
}
}
}
}
// build the list of domains that could be used with ECH (if enabled)
// so the TLS app can know to publish ECH configs for them
echDomains := make([]string, 0, len(serverDomainSet))
for d := range serverDomainSet {
echDomains = append(echDomains, d)
}
app.tlsApp.RegisterServerNames(echDomains, httpsRRALPNs(srv))View on GitHub (pinned to 50e54ee279)
Solutions
- Export the missing environment variable (e.g. `export SITE_NAME=example.com`) or define it in the Caddyfile env block before starting Caddy
- Fix the placeholder name to one the global replacer knows (env.* vars must exist at process start)
- If the value is optional, give it a default: `{$SITE_NAME:localhost}`
- Skip auto-HTTPS for that server (`auto_https off` or skip_auto_https) if you manage certs manually, so host matchers are not resolved this way
Example fix
# before
{$SITE_NAME} {
respond "hi"
}
# after (run with the var set, or default it)
{$SITE_NAME:localhost} {
respond "hi"
} Defensive patterns
Strategy: validation
Validate before calling
# Caddyfile: verify placeholders resolve before start
DOMAIN={$SITE_NAME:?SITE_NAME is not set} # fails fast in shell with message
# or use a default so resolution can never error:
# site address: {$SITE_NAME:localhost} Prevention
- Give every host-placeholder a default: {$VAR:fallback}
- Export env vars in the unit file / container env before caddy starts
- Validate configs with `caddy validate --config Caddyfile` in CI before deploy
When it happens
Trigger: A site block whose host matcher contains a placeholder like {$SITE_NAME} or {env.DOMAIN} that the global replacer cannot resolve at provision time (unset env var, unknown placeholder key, or a placeholder that resolves to empty string). Occurs only when auto-HTTPS runs, i.e. the server has HTTPS-capable addresses and skip_auto_https is not set.
Common situations: Running Caddy with a Caddyfile that uses {env.DOMAIN} as the site address but forgetting to export the variable; typos in placeholder names; using custom placeholder sources that are registered after provisioning; deploying the same config to a machine missing the env var.
Related errors
- replacing listen address: %v
- server %s: setting up route matchers: %v
- unrecognized placeholder %s%s%s
- unknown object ID '%s'
- decoding request body: %w, at offset %d
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/d479e0fb1c0482e7.
Report an issue: GitHub.