Guide
Why does Home Assistant return 400 Bad Request behind a reverse proxy?
Because the request carries `X-Forwarded-For` and the address it came from is not a trusted proxy. The log line names that address. We put Home Assistant 2026.10 behind four proxies, broke each setting on purpose, and found that the fix most threads give no longer applies.
Why does Home Assistant return 400 Bad Request behind a reverse proxy?
Home Assistant answers 400 to any request carrying an X-Forwarded-For header unless Trust X-Forwarded-For is on and the connecting address is in Trusted proxies. Since 2026.8 those settings live under Settings > System > Network > HTTP server; an http: block in configuration.yaml is imported once and then ignored. Enter the proxy's address shown in the log line, save, and confirm the new settings within 5 minutes, or Home Assistant restarts and reverts to the old ones.
Because the request carries an X-Forwarded-For header and the address it arrived from is not a trusted proxy. Home Assistant's HTTP server refuses such requests outright, and the error log names the address it saw:
ERROR (MainThread) [homeassistant.components.http.forwarded] A request from a reverse proxy was received from 10.201.13.4, but your HTTP integration is not set-up for reverse proxiesTurn on Trust X-Forwarded-For and add that address to Trusted proxies. Since 2026.8 these live under Settings > System > Network > HTTP server; the http: block forum threads tell you to paste into configuration.yaml is imported once and then ignored. Save, then confirm the new settings within 5 minutes, or Home Assistant puts the old ones back and the 400 returns.
Read the log line first: six causes, six messages#
Home Assistant's forwarded.py has six separate ways to answer 400, each with its own log line. A request without X-Forwarded-For skips all of them, which is why the same instance answers fine on http://<ip>:8123 and fails only through the proxy. We triggered every one against Home Assistant 2026.10.4 in Docker:
| Log line | Cause | Fix |
|---|---|---|
A request from a reverse proxy was received from X, but your HTTP integration is not set-up for reverse proxies | Trust X-Forwarded-For is off | Turn it on and trust X |
Received X-Forwarded-For header from an untrusted proxy X | It is on, but X is not in Trusted proxies | Add X, or its network |
Invalid IP address in X-Forwarded-For: bogus, 10.201.13.9 | Something in the chain sent a value that is not an address | See the proxy table below |
Empty item received in X-Forward-Proto header | A proxy sent an empty X-Forwarded-Proto entry | Fix the proxy's header config |
Incorrect number of elements in X-Forward-Proto. Expected 1 or 3, got 2 | X-Forwarded-Proto has neither one entry nor one per X-Forwarded-For entry | Make the proxy nearest Home Assistant send a single value |
Empty value received in X-Forward-Host header | Empty X-Forwarded-Host | Fix the proxy's header config |
The misspelt X-Forward-Proto is in the code, so search the log for that. None of our four proxies produced the last three on their own. The first two are what nearly everyone hits, and both print the address to trust.
Requests through Home Assistant Cloud's remote UI skip this check entirely, so "it works through Nabu Casa but not through my proxy" points straight here.
The two fixes that circulate, and why neither works now#
"Add use_x_forwarded_for and trusted_proxies to configuration.yaml." Correct before 2026.8, the release the documentation names for the move to the UI. The integration's config.py imports the YAML once; on every later start it is ignored and a repair appears: "HTTP YAML configuration is ignored after migration". We tested it on an instance first started without an http: block: we added one with the correct subnet, restarted the container, and requests through Caddy still got 400 with "not set-up for reverse proxies".
"trusted_proxies: [] is the minimum." That snippet is real, and it comes from Home Assistant itself: pull request 51319, merged for 2021.6, which calls it "the bare minimum configuration". Its diff shows why: the PR added a warning to the trusted networks login provider, and that check looked only at use_x_forwarded_for. The empty list silenced the warning; it never made a proxy trusted. In current code an empty list trusts nobody. Fed in through YAML on a fresh install, it produced "Received X-Forwarded-For header from an untrusted proxy" on every proxied request. The 2026.10 settings page refuses it: "at least one trusted proxy is required to use X-Forwarded-For".
The 5-minute trap: it works, then it breaks again#
Saving the HTTP server settings restarts Home Assistant on its own. Our instance answered again in under ten seconds, and the container itself never restarted. There is no reload for these settings, and no reason to run docker restart.
The new settings are a trial: the documentation says an administrator is asked to confirm them, and unconfirmed ones are reverted after 5 minutes. We left one unconfirmed. The proxy worked, then five minutes after the save the log said:
WARNING (MainThread) [homeassistant.components.http.config] Pending HTTP config was not confirmed within 0:05:00; reverting to the stable config and restartingWhen it came back up, Caddy's requests got 400 again, with "not set-up for reverse proxies". Save, test, walk away, and this is what you get. The failed trial is stored as not_promoted and not retried until you save again.
YAML on a brand new install goes the same way. A second fresh instance with a correct http: block already in configuration.yaml imported it as a trial, nobody confirmed it, and it reverted after five minutes. After that the YAML was ignored.
Which address to trust, per setup#
Trust the address in the log line, or the network it belongs to. The settings page accepts a single address (stored as /32) or a network in CIDR form, and rejects a container name ('caddy' does not appear to be an IPv4 or IPv6 network) and a host address with a mask (10.201.13.4/24 has host bits set). The documentation warns about the second: write 192.168.1.0/24, not 192.168.1.50/24. In YAML on a fresh install, that mistake failed the HTTP integration and sent Home Assistant into recovery mode, where a proxied request logs "not set-up for reverse proxies" and hides the real cause.
| Proxy | Address Home Assistant sees | What we trusted |
|---|---|---|
| Nginx Proxy Manager 2.16.0 on the compose network | NPM's container address | That address, or the network |
| Caddy 2.11.7 | Caddy's container address | Same |
| Traefik 3.7.15 | Traefik's container address | Same |
| cloudflared 2026.10.6 container | The cloudflared container, not Cloudflare | cloudflared's address |
| Proxy add-on on Home Assistant OS | An address from 172.30.33.0/24 | 172.30.33.0/24 |
| Proxy on another machine | That machine's LAN address | That one address |
nginx on the Docker host, forwarding to 127.0.0.1:8123 | The Docker network's gateway, x.x.x.1 | The gateway, exactly |
Cloudflare's IP ranges do not belong in the list. The tunnel ends at your cloudflared container, which opens an ordinary connection to Home Assistant; with only that container trusted, Home Assistant logged our real public address for a failed login. The add-on row comes from the Supervisor's source, not our lab: it builds the hassio network as 172.30.32.0/23 and assigns from 172.30.33.0/24, and issue 51986 shows the NGINX add-on at 172.30.33.2. The gateway row is the loopback case measured in Why container logs show 172.17.0.1, not the client IP.
To find a container's address and network:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' npm
docker network inspect proxy -f '{{range .IPAM.Config}}{{.Subnet}}{{end}}'Do not copy 172.16.0.0/12 or 172.18.0.0/16 from a thread. Our Docker host allocates 10.201.x.x networks, so those ranges matched nothing; Docker subnet clashes and address pools explains where your addresses come from.
One address or the whole network?#
A single address is tighter but breaks when the proxy container is recreated with a new address. A network survives that, at a price we measured. Home Assistant reads X-Forwarded-For from the right, skips every trusted entry, and takes the first untrusted one; if all are trusted, it takes the leftmost. With the whole 10.201.13.0/24 trusted, a container on that network sent a request through NPM with a forged X-Forwarded-For: 1.2.3.4, and Home Assistant logged the failed login as coming from 1.2.3.4. With only NPM's address trusted, the same request was logged from the container's real address.
Anything inside the trusted range can choose the address Home Assistant records, and IP bans act on that address. Unless the network holds only the proxy and Home Assistant, pin the proxy's address and trust it alone:
services:
npm:
image: jc21/nginx-proxy-manager:2.16.0
networks:
proxy:
ipv4_address: 10.201.13.7
networks:
proxy:
ipam:
config:
- subnet: 10.201.13.0/24A network range also always contains its gateway. Why container logs show 172.17.0.1, not the client IP shows outside IPv6 clients arriving at a proxy from that gateway when the network has no IPv6. The proxy appends the gateway, Home Assistant skips it as trusted, and takes whatever the client forged.
Why one proxy fails for some visitors and not others#
We recorded each proxy's headers with traefik/whoami, then sent Home Assistant X-Forwarded-For: bogus through each:
| Proxy | Incoming header | Sends X-Forwarded-Host | Request with X-Forwarded-For: bogus |
|---|---|---|---|
| Caddy 2.11.7 | Replaced | Yes | 200 |
| Traefik 3.7.15 | Replaced | Yes | 200 |
| NPM 2.16.0 | Appended: bogus, 10.201.13.9 | No | 400, invalid IP |
| cloudflared, through Cloudflare | Appended: bogus,<client> | No | 400, invalid IP |
NPM and Cloudflare append to the client's value, so one client behind a device that injects a bad header gets 400 while everyone else is fine. When a single user reports the error, look for this log line. The general header picture is in Reverse proxy and TLS.
When this answer stops applying#
- Before 2026.8, YAML was the place, and editing it plus a restart was the fix.
- Home Assistant OS was not in our lab; the add-on range comes from the Supervisor source.
- IPv6 proxies, HAProxy and chained proxies were not tested beyond the hand-written headers above.
- A 502 or 504 instead of a 400 comes from the proxy, not Home Assistant: Why Nginx Proxy Manager returns 502 Bad Gateway reads those.
What to do next#
Copy the address from the log line. In Settings > System > Network > HTTP server, turn on Trust X-Forwarded-For, add that address (or 172.30.33.0/24 for an add-on), save, wait for the restart, and confirm. Then delete the http: block from configuration.yaml so it stops raising a repair. If you are choosing how to reach Home Assistant from outside at all, Remote access without port forwarding weighs the options, and the Home Assistant profile lists its other upkeep.
Questions#
Why does adding use_x_forwarded_for to configuration.yaml not fix the 400?
Since 2026.8 Home Assistant imports the http: block into its own storage once, then ignores it. On our 2026.10.4 test instance, created without the block, we added it with the right subnet and restarted: requests through Caddy still got 400 with "not set-up for reverse proxies", and a repair appeared titled "HTTP YAML configuration is ignored after migration". Set Trust X-Forwarded-For and Trusted proxies under Settings > System > Network instead.
Is trusted_proxies: [] enough for a reverse proxy?
No. That snippet comes from the 2021.6 pull request that added a warning to the trusted networks login provider, which checked only use_x_forwarded_for. The request handler checks the list: with an empty list, every proxied request got 400 and "Received X-Forwarded-For header from an untrusted proxy" in our test. The 2026.10 settings page refuses an empty list with "at least one trusted proxy is required".
Do I need to restart Home Assistant after changing trusted_proxies?
Saving the HTTP server settings restarts Home Assistant by itself; in our lab it answered again within ten seconds, with no container restart. There is no reload for these settings. Then confirm them: an unconfirmed change is reverted after 5 minutes with another automatic restart, and the 400 comes back.
What trusted proxy should I use for Home Assistant OS add-ons?
172.30.33.0/24. The Supervisor creates its hassio network as 172.30.32.0/23 and hands containers addresses from 172.30.33.0/24, which is where add-ons such as the NGINX proxy appear; issue 51986 shows one at 172.30.33.2. Add-on addresses are not fixed, so the range is the practical entry. We did not run Home Assistant OS in our lab.
Why do only some visitors get 400 from Home Assistant behind Nginx Proxy Manager?
Nginx Proxy Manager appends the client's own X-Forwarded-For header instead of replacing it, and so does Cloudflare in front of cloudflared. If a client or an upstream device sends a value that is not an IP address, Home Assistant logs "Invalid IP address in X-Forwarded-For" and refuses that client only. Caddy and Traefik drop the incoming header by default, and the same request got 200.
Sources#
- Home Assistant documentation, HTTP integration
- Home Assistant core 2026.10.4, http/forwarded.py
- Home Assistant core 2026.10.4, http/config.py (YAML import, pending settings, 5-minute revert)
- Home Assistant core 2026.10.4, http/websocket_api.py (settings validation)
- home-assistant/core pull request 51319 (2021.6 reverse proxy warning)
- home-assistant/core issue 51986 (NGINX add-on at 172.30.33.2)
- Home Assistant Supervisor, const.py (hassio network ranges)
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.