Skip to content

[pull] master from home-assistant:master#386

Merged
pull[bot] merged 1 commit into
moovy2:masterfrom
home-assistant:master
Jul 14, 2026
Merged

[pull] master from home-assistant:master#386
pull[bot] merged 1 commit into
moovy2:masterfrom
home-assistant:master

Conversation

@pull

@pull pull Bot commented Jul 14, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

HTTP/3 clients send an :authority pseudo-header instead of a literal
Host header. Unlike for HTTP/2, nginx does not populate $http_host from
:authority for HTTP/3 (see nginx/nginx#455),
so "proxy_set_header Host $http_host;" evaluates to an empty value and
nginx omits the Host header on the upstream request entirely. The same
applies to X-Forwarded-Host.

Home Assistant 2026.7.0 ships aiohttp 3.14, which enforces RFC 9112
section 3.2: an HTTP/1.1 request without a Host header is rejected with
400 "Missing 'Host' header in request"
(aio-libs/aiohttp#12264). Since the proxy
speaks HTTP/1.1 upstream, HTTP/3 clients started receiving 400 errors.

The common fix is to use $host instead (as e.g. Authelia recommends,
authelia/authelia#7146), which falls
back to :authority and then server_name and is therefore never empty.
However, $host is normalized: lowercased and with the port stripped.
Home Assistant's OAuth2 discovery endpoints
(/.well-known/oauth-authorization-server,
/.well-known/oauth-protected-resource and the WWW-Authenticate header
on 401 responses, used by RFC 8414/9728 discovery, notably by MCP
connectors) match the request host *and port* against the configured
external URL. With plain $host, an external URL on a non-default port
would never match again, regressing such setups.

Instead, map the client's Host header verbatim ($http_host, which
preserves the port) and fall back to $host only when it is empty. This
keeps HTTP/1.1 and HTTP/2 behavior byte-for-byte unchanged and fixes
the missing Host header for HTTP/3.

Known limitation: for HTTP/3 clients the port is still lost in the
fallback, as nginx exposes no variable carrying the full :authority.
HTTP/3 combined with a non-default external port therefore still
cannot pass the OAuth discovery port match; this is unfixable at the
proxy layer until nginx populates the Host header from :authority for
HTTP/3 as well.

Fixes #4680

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pull pull Bot locked and limited conversation to collaborators Jul 14, 2026
@pull pull Bot added the ⤵️ pull label Jul 14, 2026
@pull pull Bot merged commit 8a16606 into moovy2:master Jul 14, 2026
1 check failed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant