Skip to content

bug(ingress): host matching is case-sensitive, mixed-case Host/hostname misroutes (RFC 4343) #1715

Description

@winklemad

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions