caddyserver/caddy · error

server listening on %v is configured for HTTP and cannot nat

Error message

server listening on %v is configured for HTTP and cannot natively multiplex HTTP and HTTPS: %s

What it means

Mirror of error 122 from the HTTPS side: the server was already classified as HTTPS (a prior address was https), and a subsequent address resolves to HTTP (`http://` scheme or the configured http_port). One listener cannot serve both protocols, so adaptation fails.

Source

Thrown at caddyconfig/httpcaddyfile/httptype.go:1168

		if httpOrHTTPS == "HTTPS" {
			errMsg := fmt.Errorf("server listening on %v is configured for HTTPS and cannot natively multiplex HTTP and HTTPS: %s",
				srv.Listen, addr.Original)
			if addr.Scheme == "" && addr.Host == "" {
				errMsg = fmt.Errorf("%s (try specifying https:// in the address)", errMsg)
			}
			return errMsg
		}
		if len(srv.TLSConnPolicies) > 0 {
			// any connection policies created for an HTTP server
			// is a logical conflict, as it would enable HTTPS
			return fmt.Errorf("server listening on %v is HTTP, but attempts to configure TLS connection policies", srv.Listen)
		}
		httpOrHTTPS = "HTTP"
		return nil
	}
	checkAndSetHTTPS := func(addr Address) error {
		if httpOrHTTPS == "HTTP" {
			return fmt.Errorf("server listening on %v is configured for HTTP and cannot natively multiplex HTTP and HTTPS: %s",
				srv.Listen, addr.Original)
		}
		httpOrHTTPS = "HTTPS"
		return nil
	}

	for _, sblock := range serverBlocks {
		for _, addr := range sblock.parsedKeys {
			if addr.Scheme == "http" || addr.Port == httpPort {
				if err := checkAndSetHTTP(addr); err != nil {
					return err
				}
			} else if addr.Scheme == "https" || addr.Port == httpsPort || len(srv.TLSConnPolicies) > 0 {
				if err := checkAndSetHTTPS(addr); err != nil {
					return err
				}
			} else if addr.Host == "" {
				if err := checkAndSetHTTP(addr); err != nil {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Move the http:// site to the HTTP port (default :80)
  2. Change the http:// address to https://
  3. Separate the sites with explicit `bind` or distinct ports so they form different servers

Example fix

# before
https://example.com {
  respond "tls"
}
http://api.example.com:443 {
  respond "api"
}
# after
https://example.com {
  respond "tls"
}
http://api.example.com:80 {
  respond "api"
}
Defensive patterns

Strategy: validation

Validate before calling

# Mirror of 122: one scheme per listener group
for group in group_addresses_by_listener(all_keys):
    schemes = {infer_scheme(a) for a in group}
    assert len(schemes) == 1

Prevention

When it happens

Trigger: A server whose first site key is `https://example.com` followed by another key/block on the same listener with `http://` scheme or a port equal to `http_port` — e.g. `https://example.com:80` after `https://example.com`, or `http://foo.example.com` sharing the https port via matching listener addresses.

Common situations: Forcing `http://` on the HTTPS port to dodge automatic HTTPS, or merging two previously separate site files into one server where one used TLS and one did not.

Related errors


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