You put Home Assistant behind the reverse proxy built into Synology DSM 7, open the hostname, and the login page appears — so the proxy looks fine. Sign in, though, and the interface hangs on the loading spinner, or connects and immediately shows Connection lost. Reconnecting… in a loop.
The proxy does work — for ordinary HTTP. Home Assistant's frontend is almost entirely driven by a WebSocket, and DSM's reverse proxy does not carry one unless you tell it to. Every normal page load succeeds and the one connection that matters never opens, which is why the symptom points nowhere useful.
The two halves of the problem
There are two independent things to fix, and fixing only one leaves you with the same blank screen:
- DSM does not upgrade the connection to a WebSocket unless you add the headers and raise the HTTP version.
- Home Assistant rejects proxied requests until you tell it which proxy to trust.
They fail at different layers and produce different evidence, so do them in order and check between the two.
Step 1. Create the reverse proxy rule
In DSM: Control Panel → Login Portal → Advanced → Reverse Proxy → Create.
| Field | Value |
|---|---|
| Reverse Proxy Name | Home Assistant |
| Source Protocol | HTTPS |
| Source Hostname | ha.example.com |
| Source Port | 443 |
| Destination Protocol | HTTP |
| Destination Hostname | 192.168.10.121 |
| Destination Port | 8123 |
The destination stays HTTP, not HTTPS. TLS terminates on the Synology; the hop from the NAS to Home Assistant is on your own LAN and Home Assistant is listening on plain 8123. Setting the destination to HTTPS here is a common and confusing mistake — it produces a 502, because nothing is listening for TLS on that port.
Leave HSTS off until the whole thing works. HSTS is remembered by the browser for months, and turning it on before you are sure of the hostname is an unpleasant thing to undo.
Step 2. Add the WebSocket headers
Still in the rule, switch to the Custom Header tab. Use Create → WebSocket — DSM has a preset for exactly this, and it adds both entries for you:
| Header Name | Value |
|---|---|
Upgrade |
$http_upgrade |
Connection |
$connection_upgrade |
Those are nginx variables, not literal text — DSM's reverse proxy is nginx underneath, and these pass the browser's upgrade request through to the backend instead of swallowing it.
Step 3. The setting everyone misses: HTTP 1.1
Switch to Advanced Settings and change Proxy HTTP version to HTTP 1.1.
This is the one that wastes people's evenings. A WebSocket upgrade cannot happen over HTTP/1.0 — the Upgrade mechanism does not exist in it. If the proxy speaks 1.0 to the backend, the two headers you just added are carried politely to a server that has no way to act on them. The configuration looks right and behaves exactly as if you had configured nothing.
While you are on that tab, the timeouts matter too:
| Setting | Value |
|---|---|
| Proxy connection timeout | 60 |
| Proxy send timeout | 60 |
| Proxy read timeout | 60 |
A WebSocket is a long-lived connection that is idle most of the time. With a short read timeout the proxy closes it on schedule and the frontend reconnects, over and over — which looks like an unstable network rather than a configured timeout. Sixty seconds is enough because Home Assistant sends its own keepalives well inside that window.
Tick Use the error page sent back by target server as well, so a real backend error reaches you instead of a generic DSM page. You want to see Home Assistant's own 400 in the next step.
Save, and DSM reloads nginx on its own.
Step 4. Tell Home Assistant to trust the proxy
Now the other half. Left alone, Home Assistant refuses requests that arrive with proxy headers, complaining that "your HTTP integration is not set-up for reverse proxies", logged exactly like this:
A request from a reverse proxy was received from 192.168.10.5, but your HTTP
integration is not set-up for reverse proxies
The request is answered with HTTP 400, so the browser shows a bare error rather than anything that hints at the cause. The address in the message is your NAS — that is the value you are about to whitelist.
From Home Assistant 2026.8 onward these settings live in the UI, not in configuration.yaml: Settings → System → Network, then turn on Trust X-Forwarded-For and add the proxy's address under Trusted proxies.
On older installs the same two settings are YAML keys, and most guides still show this form:
http:
use_x_forwarded_for: true
trusted_proxies:
- 192.168.10.5
Use the address of the Synology itself, not the network the browsers come from. Restart Home Assistant after a YAML change; the UI settings apply without one.
If you are worried about locking yourself out with a wrong entry, do not be: saving these settings in the UI restarts Home Assistant and then asks an administrator to confirm them. If nobody confirms within five minutes, it reverts to what was there before.
Keep the trusted list as small as it can be. Trusting a proxy means believing whatever it says about who the client is — a broad range here lets anything on that range claim any source address, which matters if you use trusted-network access at all.
Verify
Check the two halves separately, because they fail separately.
That the plain HTTP path works:
curl -sI https://ha.example.com/ | head -3
A 200 means the proxy and the certificate are fine — and, as established, it means very little about the WebSocket.
That the upgrade actually happens:
curl -si https://ha.example.com/api/websocket \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
| head -5
You want HTTP/1.1 101 Switching Protocols. Anything else narrows it down — but read the Home Assistant log alongside the status code, because the two different 400s look identical from the outside and have completely different causes:
| Response | Cause |
|---|---|
101 Switching Protocols |
working |
400, and the log says not set-up for reverse proxies |
Home Assistant does not trust the proxy — Step 4 |
400, with nothing about proxies in the log |
the upgrade headers never reached Home Assistant — Step 2 |
502, a plain 200 with the HTML page, or a reset connection |
the proxy is still speaking HTTP/1.0, or the destination protocol or port is wrong — Steps 1 and 3 |
That last row is deliberately vague about which one you get, because it varies: an upgrade attempted over an HTTP/1.0 upstream can fail in more than one way depending on where the protocol switch gives up. Treat any of those three as "go back and check Steps 1 and 3" rather than as a precise diagnosis.
Then open the interface in a browser, sign in, and leave the tab alone for several minutes. A WebSocket problem that survives Step 3 usually shows up as a reconnect loop on a timer rather than an immediate failure, so a page that looks fine for thirty seconds has not proved much.
Before you point this at the internet
Reaching Home Assistant from outside means the login page is reachable from everywhere, so treat the proxy as the easy part and the exposure as the real decision:
- Turn on multi-factor authentication for every account that can log in.
- Turn on Enable IP banning and set a low Login attempts before ban under Settings → System → Network — the YAML equivalents are
ip_ban_enabledandlogin_attempts_threshold. It is the same discipline as catching SSH brute force with CSF and lfd, applied to a web login instead of a shell. - Consider not exposing it at all. A VPN or a mesh network such as Tailscale gives you the same access with nothing published — and it is markedly less work than keeping a public login page safe.
If you do publish it, the certificate on the Synology and the trusted-proxy list are the only two things standing between the internet and your house.