caddyserver/caddy · error

upstream address has conflicting scheme (https://) and port

Error message

upstream address has conflicting scheme (https://) and port (:80, the HTTP port)

What it means

The upstream declares scheme https:// but an explicit port 80 (the plaintext HTTP default). This is the mirror of the http://:443 conflict: Caddy would be asked to speak TLS to a plaintext port and fails fast at config load.

Source

Thrown at modules/caddyhttp/reverseproxy/addresses.go:115

			}
		}
		if port == "" {
			port = toURL.Port()
		}

		// there is currently no way to perform a URL rewrite between choosing
		// a backend and proxying to it, so we cannot allow extra components
		// in backend URLs
		if toURL.Path != "" || toURL.RawQuery != "" || toURL.Fragment != "" {
			return parsedAddr{}, fmt.Errorf("for now, URLs for proxy upstreams only support scheme, host, and port components")
		}

		// ensure the port and scheme aren't in conflict
		if toURL.Scheme == "http" && port == "443" {
			return parsedAddr{}, fmt.Errorf("upstream address has conflicting scheme (http://) and port (:443, the HTTPS port)")
		}
		if toURL.Scheme == "https" && port == "80" {
			return parsedAddr{}, fmt.Errorf("upstream address has conflicting scheme (https://) and port (:80, the HTTP port)")
		}
		if toURL.Scheme == "h2c" && port == "443" {
			return parsedAddr{}, fmt.Errorf("upstream address has conflicting scheme (h2c://) and port (:443, the HTTPS port)")
		}

		// if port is missing, attempt to infer from scheme
		if port == "" {
			switch toURL.Scheme {
			case "", "http", "h2c":
				port = "80"
			case "https":
				port = "443"
			}
		}

		scheme, host = toURL.Scheme, toURL.Hostname()
	} else {
		var err error

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Use 'http://backend:80' (or just 'backend:80') when the backend is plaintext.
  2. Point the https:// upstream at the actual TLS port (443 or custom).
  3. If TLS terminates upstream of the backend, terminate it in Caddy instead and proxy plaintext.

Example fix

# before
reverse_proxy https://backend:80

# after
reverse_proxy http://backend:80
Defensive patterns

Strategy: validation

Validate before calling

func httpsOnHttpPort(u *url.URL) bool {
    return u.Scheme == "https" && u.Port() == "80"
}

if parsed, err := url.Parse(upstream); err == nil && httpsOnHttpPort(parsed) {
    return fmt.Errorf("upstream %q pairs https:// with the plaintext port 80", upstream)
}

Prevention

When it happens

Trigger: 'reverse_proxy https://backend:80' or 'caddy reverse-proxy --to https://host:80'.

Common situations: Backend behind a TLS-terminating load balancer that listens on 80 while the config kept https://; mixing up internal (plaintext) and external (TLS) ports.

Related errors


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