When the fix doesn't fix it: a Host header, a router, and a patched nginx
Wanted to reach my Livebox's admin UI from outside my home network, through the nginx-based reverse proxy (npmplus, an nginx-proxy-manager fork) I already run for everything else. Two problems showed up back to back, and the second one took a lot longer to pin down than the first.
Problem 1: the Livebox rejects requests it doesn't recognize
Any request through the proxy came back as an immediate connection reset. Testing directly against the Livebox confirmed it: any HTTPS request whose Host header didn't exactly match its own IP got dropped before a response was even sent. This is anti-DNS-rebinding protection, common on ISP-supplied routers — the router treats a mismatched Host header as a sign someone is trying to trick a browser into managing it from the outside.
Fix: rewrite the Host header before it leaves the proxy, so the Livebox sees a request for 192.168.1.254, not for the public hostname:
proxy_set_header Host 192.168.1.254;
proxy_ssl_server_name off;
That cleared the reset. Progress — but not the finish line.
Problem 2: still failing, with a much sneakier cause
With the Host header fixed, the proxy now returned 502 Bad Gateway — "upstream prematurely closed connection while reading response header." Replaying the exact same request by hand with openssl s_client, same headers, same HTTP version, worked every time: full page, correct size. Through the proxy, it kept failing.
A few rounds of isolating variables (HTTP version, TLS version, cipher suite, ALPN, buffering, keepalive) ruled out anything to do with what the proxy was sending. A packet capture showed the TCP/TLS session between the proxy and the Livebox completing cleanly — no reset, just less data coming back than a full page should contain.
The deciding test: standing up a bare, minimal nginx with an equivalent config, on a different host, worked immediately. That isolated the problem away from "nginx" in general and onto something specific to this particular proxy's setup.
The proxy manager in question ships a custom-patched nginx build — specifically, one patched to support the nginx-plus-only dynamic upstream resolve feature, so upstream targets can be re-resolved on the fly. That patched code path turned out to be the actual bug. Replacing the generated upstream { ... resolve; } block with a direct proxy_pass straight to the IP fixed it immediately:
location ~ ^/ {
proxy_pass https://192.168.1.254$request_uri;
proxy_set_header Host 192.168.1.254;
proxy_ssl_server_name off;
}
Takeaways
- If a reverse-proxied request to a router/appliance admin UI gets silently reset, check the
Hostheader first — a lot of consumer routers guard against exactly this. - When a fix doesn't fix it, don't assume the theory was wrong — check whether you're still testing the same code path. A working manual replay next to a failing proxied one is a strong signal something below the config layer is involved, not just above it.
- Custom-patched software carries custom-patched bugs. A feature borrowed from a commercial fork (in this case, nginx-plus's dynamic upstream resolution) is exactly the kind of code path that gets less real-world mileage than the stable core it's bolted onto.