caddyserver/caddy · error
[%s] scheme and port violate convention
Error message
[%s] scheme and port violate convention
What it means
The resolved listener port contradicts the key's scheme: http:// on the HTTPS port, or https:// on the HTTP port (defaults 443/80, or the http_port/https_port globals). Caddy enforces the convention to catch config mistakes that would otherwise serve TLS on the wrong port silently.
Source
Thrown at caddyconfig/httpcaddyfile/addresses.go:306
httpPort = strconv.Itoa(hport.(int))
}
if hsport, ok := options["https_port"]; ok {
httpsPort = strconv.Itoa(hsport.(int))
}
// default port is the HTTPS port
lnPort := httpsPort
if addr.Port != "" {
// port explicitly defined
lnPort = addr.Port
} else if addr.Scheme == "http" {
// port inferred from scheme
lnPort = httpPort
}
// error if scheme and port combination violate convention
if (addr.Scheme == "http" && lnPort == httpsPort) || (addr.Scheme == "https" && lnPort == httpPort) {
return nil, fmt.Errorf("[%s] scheme and port violate convention", addr.String())
}
// the bind directive specifies hosts (and potentially network), and the protocols to serve them with, but is optional
lnCfgVals := make([]addressesWithProtocols, 0, len(sblock.pile["bind"]))
for _, cfgVal := range sblock.pile["bind"] {
if val, ok := cfgVal.Value.(addressesWithProtocols); ok {
lnCfgVals = append(lnCfgVals, val)
}
}
if len(lnCfgVals) == 0 {
if defaultBindValues, ok := options["default_bind"].([]ConfigValue); ok {
for _, defaultBindValue := range defaultBindValues {
lnCfgVals = append(lnCfgVals, defaultBindValue.Value.(addressesWithProtocols))
}
} else {
lnCfgVals = []addressesWithProtocols{{
addresses: []string{""},
protocols: nil,View on GitHub (pinned to 50e54ee279)
Solutions
- Align scheme and port: use https with 443 (or your https_port) and http with 80 (or your http_port)
- If you truly need HTTP on the HTTPS port, omit the scheme but keep the port, or re-map ports consistently in the globals
- Check the global options block for http_port/https_port overrides that interact with the key
Example fix
# before
http://example.com:443 {
}
# after
https://example.com:443 {
} Defensive patterns
Strategy: validation
Validate before calling
httpPort, httpsPort := "80", "443" // or your overrides
port := addr.Port
if port == "" { port = map[bool]string{true: httpPort, false: httpsPort}[addr.Scheme == "http"] }
if (addr.Scheme == "http" && port == httpsPort) || (addr.Scheme == "https" && port == httpPort) {
return fmt.Errorf("%s mixes scheme and port", addr.String())
} Prevention
- Keep scheme/port pairs canonical: http=80/http_port, https=443/https_port
- When overriding http_port/https_port globals, update all site labels consistently
- Omit the port on site labels when using defaults
When it happens
Trigger: Keys like 'http://example.com:443' or 'https://example.com:80', or with globals http_port 443 / https_port 80 set, any http key without explicit port resolves to the https port and trips this.
Common situations: Running HTTP explicitly on 443 during testing, or swapping http_port/https_port globals to dodge a port conflict without updating site labels.
Related errors
- the scheme wss:// is only supported in browsers; use https:/
- the scheme ws:// is only supported in browsers; use http://
- unsupported URL scheme %s://
- parsing key: %v
- server block %d, key %d (%s): determining listener address:
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/a0a6bfd62784260c.
Report an issue: GitHub.