Describe the bug
Ingress rule matching is case-sensitive, but hostnames are case-insensitive (RFC 4343; RFC 3986 §3.2.2 — "the host subcomponent is case-insensitive"). matchHost in ingress/ingress.go compares with == and strings.HasSuffix without normalizing case, and there is no case-normalization anywhere else in the ingress package:
func matchHost(ruleHost, reqHost string) bool {
if ruleHost == reqHost {
return true
}
if strings.HasPrefix(ruleHost, "*.") {
toMatch := strings.TrimPrefix(ruleHost, "*")
return strings.HasSuffix(reqHost, toMatch)
}
return false
}
So any case difference between a rule's hostname and the request Host makes the request skip the intended rule and fall through to the catch-all (503 or the wrong origin).
This is reachable two ways:
proxy/proxy.go passes req.Host (which can legitimately be mixed-case) straight into FindMatchingRule un-lowercased.
validateHostname accepts mixed-case hostnames without lowercasing them, so a config such as hostname: MyApp.example.com is accepted but never matches myapp.example.com.
To Reproduce
cloudflared tunnel ingress rule (which passes url.Hostname() straight to FindMatchingRule) shows it, with no network needed. Given a config:
ingress:
- hostname: myapp.example.com
service: http://localhost:8080
- service: http_status:404
$ cloudflared tunnel ingress rule https://MyApp.example.com/
reports the catch-all rule (http_status:404) instead of myapp.example.com. A unit test on matchHost/FindMatchingRule with a mixed-case host fails on main.
Expected behavior
Hostnames match case-insensitively, like every other HTTP router (nginx, Caddy, Envoy, Traefik).
Fix
Normalize case in matchHost (lower-case both sides before comparing). I have a small patch + tests ready and would like to open a PR.
Describe the bug
Ingress rule matching is case-sensitive, but hostnames are case-insensitive (RFC 4343; RFC 3986 §3.2.2 — "the host subcomponent is case-insensitive").
matchHostiningress/ingress.gocompares with==andstrings.HasSuffixwithout normalizing case, and there is no case-normalization anywhere else in the ingress package:So any case difference between a rule's hostname and the request Host makes the request skip the intended rule and fall through to the catch-all (503 or the wrong origin).
This is reachable two ways:
proxy/proxy.gopassesreq.Host(which can legitimately be mixed-case) straight intoFindMatchingRuleun-lowercased.validateHostnameaccepts mixed-case hostnames without lowercasing them, so a config such ashostname: MyApp.example.comis accepted but never matchesmyapp.example.com.To Reproduce
cloudflared tunnel ingress rule(which passesurl.Hostname()straight toFindMatchingRule) shows it, with no network needed. Given a config:reports the catch-all rule (
http_status:404) instead ofmyapp.example.com. A unit test onmatchHost/FindMatchingRulewith a mixed-case host fails onmain.Expected behavior
Hostnames match case-insensitively, like every other HTTP router (nginx, Caddy, Envoy, Traefik).
Fix
Normalize case in
matchHost(lower-case both sides before comparing). I have a small patch + tests ready and would like to open a PR.