caddyserver/caddy · error
server %s: listener protocols count does not match address c
Error message
server %s: listener protocols count does not match address count: %d != %d
What it means
Per-server validation in the http app: when listen_protocols is set, its slice length must equal the length of the listen addresses slice, because the two are index-paired. A mismatch aborts provisioning with both counts printed.
Source
Thrown at modules/caddyhttp/app.go:258
app.Metrics.PerHost = app.Metrics.PerHost || srv.Metrics.PerHost
}
// only enable access logs if configured
if srv.Logs != nil {
srv.accessLogger = app.logger.Named("log.access")
if srv.Logs.Trace {
srv.traceLogger = app.logger.Named("log.trace")
}
}
// if no protocols configured explicitly, enable all except h2c
if len(srv.Protocols) == 0 {
srv.Protocols = srv.protocolsWithDefaults()
}
if srv.ListenProtocols != nil {
if len(srv.ListenProtocols) != len(srv.Listen) {
return fmt.Errorf("server %s: listener protocols count does not match address count: %d != %d",
srvName, len(srv.ListenProtocols), len(srv.Listen))
}
for i, lnProtocols := range srv.ListenProtocols {
if lnProtocols != nil {
srv.ListenProtocols[i] = srv.listenerProtocolsWithDefaults(lnProtocols)
}
}
}
// limit max header bytes to a more reasonable default than 1MB from Go std lib
// (see https://github.com/php/frankenphp/issues/2459#issuecomment-4655612909)
if srv.MaxHeaderBytes <= 0 {
srv.MaxHeaderBytes = 16 * 1024
}
// if not explicitly configured by the user, disallow TLS
// client auth bypass (domain fronting) which couldView on GitHub (pinned to 50e54ee279)
Solutions
- Make the arrays the same length: one protocols entry (possibly null for defaults) per listen address, in the same order.
- Or drop listen_protocols entirely to use the default protocol set on all listeners.
- Use null (JSON) / leave an empty entry for any listener that should keep defaults.
Example fix
// before (caddyfile)
srv0 {
listen :443 :80
listen_protocols h1 h2 h3
}
// after
srv0 {
listen :443 :80
listen_protocols h1 h2 h3 h1 h2
} Defensive patterns
Strategy: validation
Validate before calling
// before load: arrays must be index-paired
if len(srv.ListenProtocols) != 0 && len(srv.ListenProtocols) != len(srv.Listen) {
return fmt.Errorf("listen_protocols has %d entries but listen has %d addresses", len(srv.ListenProtocols), len(srv.Listen))
} Try / catch
if err := caddy.Validate(cfg); err != nil {
if strings.Contains(err.Error(), "listener protocols count does not match address count") {
// align the two arrays (or drop listen_protocols) and re-validate
}
return err
} Prevention
- Edit listen and listen_protocols together — they are positionally paired.
- Omit listen_protocols when the default protocol set is acceptable everywhere.
- Add a config lint rule that asserts equal lengths of the two arrays.
When it happens
Trigger: server { listen [":443", ":80"] listen_protocols [["h1","h2","h3"]] } — 2 addresses but 1 protocols entry. Also triggered by JSON configs with mismatched arrays, or removing a listen address without updating protocols.
Common situations: Adding/removing a listen address and forgetting listen_protocols; hand-edited JSON; copy-paste between server blocks with different address counts.
Related errors
- malformed If-Match header; expect quoted string
- malformed If-Match header; expect format \"<path> <hash>\"
- unsupported network type: %s
- loading trusted proxies modules: %v
- invalid action type
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/af4d5654076c0178.
Report an issue: GitHub.