{"record":{"id":"c432c5ae616e95b9","repo":"caddyserver/caddy","slug":"upstream-address-has-conflicting-scheme-http","errorCode":null,"errorMessage":"upstream address has conflicting scheme (http://) and port (:443, the HTTPS port)","messagePattern":"upstream address has conflicting scheme \\(http://\\) and port \\(:443, the HTTPS port\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/reverseproxy/addresses.go","lineNumber":112,"sourceCode":"\t\t\t\tport = portRange\n\t\t\t} else {\n\t\t\t\treturn parsedAddr{}, fmt.Errorf(\"parsing upstream URL: %v\", err)\n\t\t\t}\n\t\t}\n\t\tif port == \"\" {\n\t\t\tport = toURL.Port()\n\t\t}\n\n\t\t// there is currently no way to perform a URL rewrite between choosing\n\t\t// a backend and proxying to it, so we cannot allow extra components\n\t\t// in backend URLs\n\t\tif toURL.Path != \"\" || toURL.RawQuery != \"\" || toURL.Fragment != \"\" {\n\t\t\treturn parsedAddr{}, fmt.Errorf(\"for now, URLs for proxy upstreams only support scheme, host, and port components\")\n\t\t}\n\n\t\t// ensure the port and scheme aren't in conflict\n\t\tif toURL.Scheme == \"http\" && port == \"443\" {\n\t\t\treturn parsedAddr{}, fmt.Errorf(\"upstream address has conflicting scheme (http://) and port (:443, the HTTPS port)\")\n\t\t}\n\t\tif toURL.Scheme == \"https\" && port == \"80\" {\n\t\t\treturn parsedAddr{}, fmt.Errorf(\"upstream address has conflicting scheme (https://) and port (:80, the HTTP port)\")\n\t\t}\n\t\tif toURL.Scheme == \"h2c\" && port == \"443\" {\n\t\t\treturn parsedAddr{}, fmt.Errorf(\"upstream address has conflicting scheme (h2c://) and port (:443, the HTTPS port)\")\n\t\t}\n\n\t\t// if port is missing, attempt to infer from scheme\n\t\tif port == \"\" {\n\t\t\tswitch toURL.Scheme {\n\t\t\tcase \"\", \"http\", \"h2c\":\n\t\t\t\tport = \"80\"\n\t\t\tcase \"https\":\n\t\t\t\tport = \"443\"\n\t\t\t}\n\t\t}\n","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/reverseproxy/addresses.go#L94-L130","documentation":"The upstream declares scheme http:// (cleartext) but an explicit port 443 (HTTPS default). Caddy treats this as a probable misconfiguration and refuses to start rather than silently sending plaintext HTTP to a TLS listener.","triggerScenarios":"'reverse_proxy http://backend:443' or 'caddy reverse-proxy --from x --to http://host:443'.","commonSituations":"Forgetting to change the scheme when the backend was upgraded to TLS, or assuming Caddy upgrades the connection; also copying a port from an https:// URL while typing http://.","solutions":["If the backend is TLS-enabled, use 'https://backend:443' (or just 'https://backend').","If the backend really serves plaintext on 443, pick the correct port or drop the scheme so the conflict disappears.","Re-validate after the change: 'caddy validate --config <file>'."],"exampleFix":"# before\nreverse_proxy http://backend:443\n\n# after\nreverse_proxy https://backend:443","handlingStrategy":"validation","validationCode":"var tlsPorts = map[string]bool{\"443\": true}\nfunc schemePortConflict(u *url.URL) bool {\n    return (u.Scheme == \"http\" || u.Scheme == \"h2c\") && tlsPorts[u.Port()]\n}\n\nif parsed, err := url.Parse(upstream); err == nil && schemePortConflict(parsed) {\n    return fmt.Errorf(\"upstream %q pairs a cleartext scheme with the HTTPS port\", upstream)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep scheme and port semantically aligned: http/h2c -> non-443, https -> 443/custom TLS port.","When a backend moves to TLS, change the scheme in the same commit as the port.","CI-validate configs to catch drift between scheme and port."],"tags":["reverse-proxy","upstream","tls","port-conflict"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}