envoyproxy / envoyproxy/gateway

Health check Host header should use backend endpoint FQDN when auto-host-rewrite is configured

Open
#8,848 4 comments 0 reactions 0 assignees View on GitHub
stale
Dominant language
Go
Stars
3k
Forks
864
Avg merge
2d 2h
Merged PRs (30d)
140

Description

**What happened:**

When a Backend has multiple FQDN endpoints and the HTTPRoute uses `hostname.type: Backend` (auto-host-rewrite), active health checks still send the **route hostname** as the `Host` header instead of the **backend endpoint FQDN**. This causes health checks to fail for external backends that expect the correct `Host` header.

**Root cause:**

In `backendtrafficpolicy.go`, `SetHTTPHostIfAbsent(r.Hostname)` always sets the health check `HTTP.Host` to the route hostname. The xDS translator's `getHealthCheckOverridesHostname()` (added in #8452) supports per-endpoint hostname fallback, but only when `HTTP.Host` is empty — which it never is, because `SetHTTPHostIfAbsent` always fills it first.

So #8452 fixed the xDS translator to support per-endpoint health check hostnames, but the gatewayapi layer always pre-fills `HTTP.Host`, making the per-endpoint fallback dead code for HTTP health checks.

**Expected behavior:**

When `hostname.type: Backend` is configured on the route, health checks should use each endpoint's FQDN as the `Host` header. This mirrors how `auto_host_rewrite` works for request forwarding — the Host header is rewritten to the upstream hostname. The same should apply to health checks.

**Proposed fix:**

1. Add `UseEndpointHostname bool` to the IR `HealthCheck` struct — set to `true` when the route has `hostname.type: Backend` configured
2. Auto-derive `DestinationEndpoint.Hostname` from `FQDN.Hostname` when not explicitly set — so the endpoint always has a hostname available
3. In `buildHealthCheckConfig`, when `UseEndpointHostname` is true, set per-endpoint `HealthCheckConfig.Hostname` to the endpoint FQDN — this overrides the cluster-level Host in Envoy

`SetHTTPHostIfAbsent` still runs, so the cluster-level Host is set as a fallback and IR validation passes. The per-endpoint hostname simply overrides it in Envoy when the flag is set.

This is non-breaking: routes without `hostname.type: Backend` continue to use the route hostname for health checks (current behavior unchanged).

**Scenarios:**

| Config | Health check Host header |
|---|---|
| FQDN backend + `hostname.type: Backend` | Per-endpoint FQDN (NEW) |
| FQDN backend, no auto-host-rewrite | Route hostname (unchanged) |
| FQDN backend + explicit `http.hostname` | User's explicit value (unchanged) |
| Service backend (internal) | Route hostname (unchanged) |

**Related:**
- #7798 (original health check hostname issue, closed by #8452)
- #8452 (added per-endpoint hostname support in xDS, but bypassed by `SetHTTPHostIfAbsent`)
- [Comment by @Aditya7880900936](https://github.com/envoyproxy/gateway/issues/7798#issuecomment-3692815230) proposing the auto-fallback approach

/cc @arkodg @zirain

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.