Summary
After upgrading HAProxy Unified Gateway from chart 1.0.2 (app 1.0.2) to chart 1.0.5 (app 1.0.3), public routes for shared hostnames started returning 404 NOT FOUND from HUG backend_not_found.
This reproduces when multiple HTTPRoute objects use the same hostname on the same listener (for example domain.tld split across two routes).
Impact
- User-facing routes on shared hostnames became unavailable.
- Example:
api.domain.tld continued to work.
domain.tld and preview.domain.tld returned 404 NOT FOUND.
Environment
- Kubernetes client used during investigation:
kubectl v1.34.1
- HUG chart upgraded to:
haproxy-unified-gateway-1.0.5
- HUG app version in that chart:
1.0.3
- Previous working state: chart
1.0.2 (app 1.0.2)
Relevant Configuration
Gateway listener (public)
Public gateway has a listener without hostname, routes selected by namespace labels:
Gateway: haproxy-unified-gateway/public-http
- listener:
http on port 20080
Routes (problematic shape)
Multiple routes share the same hostname on the same listener:
HTTPRoute app/app-front
- hostnames:
domain.tld
- paths:
/
HTTPRoute app/app-admin
- hostnames:
domain.tld
- paths:
/admin, /admin/static
Same pattern exists for preview:
app-preview/app-front and app-preview/app-admin both use preview.domain.tld.
Observed Behavior
Request behavior from public HUG pod
Host: api.domain.tld -> 200
Host: whoami.domain.tld -> 200
Host: domain.tld -> 404
Host: preview.domain.tld -> 404
HUG-generated map content (public controller)
/usr/local/hug/maps/hug_http_20080/listener_route_exact_match.map includes duplicate key rows for the same hostname:
haproxy-unified-gateway/public-http_http/domain.tld haproxy-unified-gateway/public-http_http/app/app-admin
haproxy-unified-gateway/public-http_http/domain.tld haproxy-unified-gateway/public-http_http/app/app-front
And similarly for preview.domain.tld.
HAProxy access log evidence
For failing domain.tld requests, the log shows LR (selected listener route) as a comma-separated list instead of a single route:
LR:haproxy-unified-gateway/public-http_http/app/app-admin,haproxy-unified-gateway/public-http_http/app/app-front
Then request falls through to:
hug_http_20080 backend_not_found/<NOSRV> ... 404 ... "GET domain.tld/ HTTP/1.1"
This indicates route selection becomes ambiguous/non-scalar and path lookup fails.
Comparison with 1.0.2 (working behavior)
1.0.2 map/config model observed
On chart 1.0.2 (app 1.0.2), public listener uses direct host+path lookups and does not use listener_route_exact_match.map:
show map:
/usr/local/hug/maps/hug_http_20080/path_exact.map
/usr/local/hug/maps/hug_http_20080/path_prefix.map
/usr/local/hug/maps/hug_http_20080/domain_wildcard_path_exact.map
/usr/local/hug/maps/hug_http_20080/path_regex.map
/usr/local/hug/haproxy.cfg route chain:
set-var(txn.base) var(txn.host),concat("",txn.path)
set-var(txn.route) var(txn.base),map(path_exact.map)
set-var(txn.route,ifnotexists) path,map(path_exact.map)
set-var(txn.route,ifnotexists) var(txn.base),map_beg(path_prefix.map)
set-var(txn.route,ifnotexists) path,map_beg(path_prefix.map)
...
use_backend %[var(txn.route)]
Chart 1.0.2 request outcomes for the same route layout
With the same split-hostname layout:
GET domain.tld/ -> 200 (front backend selected)
GET domain.tld/admin -> 200 (admin backend selected)
GET preview.domain.tld/ -> 200
GET preview.domain.tld/admin -> 200
So the split-hostname routes are handled correctly on 1.0.2, but not on 1.0.3.
Why this looks like a regression
- Same route layout previously worked on chart
1.0.2 (app 1.0.2).
- Regression appears after moving to chart
1.0.5 (app 1.0.3) which introduced new map/listener selection behavior.
- Failure is specific to hostnames shared by multiple
HTTPRoute resources; unique hostnames still route correctly.
Repro Steps
- Install HUG chart
1.0.5 (app 1.0.3) with an HTTP listener without explicit hostname.
- Create two
HTTPRoutes attached to that listener with the same hostname:
- Route A: hostname
example.com, path /
- Route B: hostname
example.com, path /admin
- Send requests with
Host: example.com to / and /admin.
- Inspect:
- runtime map files under
/usr/local/hug/maps/...
- HAProxy access log fields
L, LR, BLR
Expected
- Deterministic route selection and correct path dispatch:
/ -> Route A backend
/admin -> Route B backend
Actual
- Requests return
404 NOT FOUND from backend_not_found.
LR/BLR become malformed/ambiguous (comma-joined multi-route value).
Summary
After upgrading HAProxy Unified Gateway from chart
1.0.2(app1.0.2) to chart1.0.5(app1.0.3), public routes for shared hostnames started returning404 NOT FOUNDfrom HUGbackend_not_found.This reproduces when multiple
HTTPRouteobjects use the same hostname on the same listener (for exampledomain.tldsplit across two routes).Impact
api.domain.tldcontinued to work.domain.tldandpreview.domain.tldreturned404 NOT FOUND.Environment
kubectl v1.34.1haproxy-unified-gateway-1.0.51.0.31.0.2(app1.0.2)Relevant Configuration
Gateway listener (public)
Public gateway has a listener without
hostname, routes selected by namespace labels:Gateway:haproxy-unified-gateway/public-httphttpon port20080Routes (problematic shape)
Multiple routes share the same hostname on the same listener:
HTTPRoute app/app-frontdomain.tld/HTTPRoute app/app-admindomain.tld/admin,/admin/staticSame pattern exists for preview:
app-preview/app-frontandapp-preview/app-adminboth usepreview.domain.tld.Observed Behavior
Request behavior from public HUG pod
Host: api.domain.tld->200Host: whoami.domain.tld->200Host: domain.tld->404Host: preview.domain.tld->404HUG-generated map content (public controller)
/usr/local/hug/maps/hug_http_20080/listener_route_exact_match.mapincludes duplicate key rows for the same hostname:And similarly for
preview.domain.tld.HAProxy access log evidence
For failing
domain.tldrequests, the log showsLR(selected listener route) as a comma-separated list instead of a single route:Then request falls through to:
This indicates route selection becomes ambiguous/non-scalar and path lookup fails.
Comparison with 1.0.2 (working behavior)
1.0.2 map/config model observed
On chart
1.0.2(app1.0.2), public listener uses direct host+path lookups and does not uselistener_route_exact_match.map:/usr/local/hug/haproxy.cfgroute chain:Chart 1.0.2 request outcomes for the same route layout
With the same split-hostname layout:
GET domain.tld/->200(front backend selected)GET domain.tld/admin->200(admin backend selected)GET preview.domain.tld/->200GET preview.domain.tld/admin->200So the split-hostname routes are handled correctly on
1.0.2, but not on1.0.3.Why this looks like a regression
1.0.2(app1.0.2).1.0.5(app1.0.3) which introduced new map/listener selection behavior.HTTPRouteresources; unique hostnames still route correctly.Repro Steps
1.0.5(app1.0.3) with an HTTP listener without explicit hostname.HTTPRoutes attached to that listener with the same hostname:example.com, path/example.com, path/adminHost: example.comto/and/admin./usr/local/hug/maps/...L,LR,BLRExpected
/-> Route A backend/admin-> Route B backendActual
404 NOT FOUNDfrombackend_not_found.LR/BLRbecome malformed/ambiguous (comma-joined multi-route value).