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

  1. Align scheme and port: use https with 443 (or your https_port) and http with 80 (or your http_port)
  2. If you truly need HTTP on the HTTPS port, omit the scheme but keep the port, or re-map ports consistently in the globals
  3. 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

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


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