<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Laurent's Homelab Notes Blog</title>
        <link>https://blog.ltfi.fr/</link>
        <description>Laurent's Homelab Notes Blog</description>
        <lastBuildDate>Mon, 13 Jul 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[When the fix doesn't fix it: a Host header, a router, and a patched nginx]]></title>
            <link>https://blog.ltfi.fr/when-the-fix-doesnt-fix-it</link>
            <guid>https://blog.ltfi.fr/when-the-fix-doesnt-fix-it</guid>
            <pubDate>Mon, 13 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[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.]]></description>
            <content:encoded><![CDATA[<p>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.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="problem-1-the-livebox-rejects-requests-it-doesnt-recognize">Problem 1: the Livebox rejects requests it doesn't recognize<a href="https://blog.ltfi.fr/when-the-fix-doesnt-fix-it#problem-1-the-livebox-rejects-requests-it-doesnt-recognize" class="hash-link" aria-label="Direct link to Problem 1: the Livebox rejects requests it doesn't recognize" title="Direct link to Problem 1: the Livebox rejects requests it doesn't recognize" translate="no">​</a></h2>
<p>Any request through the proxy came back as an immediate connection reset. Testing directly against the Livebox confirmed it: any HTTPS request whose <code>Host</code> 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.</p>
<p>Fix: rewrite the Host header before it leaves the proxy, so the Livebox sees a request for <code>192.168.1.254</code>, not for the public hostname:</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">proxy_set_header Host 192.168.1.254;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">proxy_ssl_server_name off;</span><br></div></code></pre></div></div>
<p>That cleared the reset. Progress — but not the finish line.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="problem-2-still-failing-with-a-much-sneakier-cause">Problem 2: still failing, with a much sneakier cause<a href="https://blog.ltfi.fr/when-the-fix-doesnt-fix-it#problem-2-still-failing-with-a-much-sneakier-cause" class="hash-link" aria-label="Direct link to Problem 2: still failing, with a much sneakier cause" title="Direct link to Problem 2: still failing, with a much sneakier cause" translate="no">​</a></h2>
<p>With the Host header fixed, the proxy now returned <code>502 Bad Gateway</code> — "upstream prematurely closed connection while reading response header." Replaying the exact same request by hand with <code>openssl s_client</code>, same headers, same HTTP version, worked every time: full page, correct size. Through the proxy, it kept failing.</p>
<p>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.</p>
<p>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.</p>
<p>The proxy manager in question ships a custom-patched nginx build — specifically, one patched to support the nginx-plus-only dynamic upstream <code>resolve</code> 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 <code>upstream { ... resolve; }</code> block with a direct <code>proxy_pass</code> straight to the IP fixed it immediately:</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">location ~ ^/ {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    proxy_pass https://192.168.1.254$request_uri;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    proxy_set_header Host 192.168.1.254;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    proxy_ssl_server_name off;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="takeaways">Takeaways<a href="https://blog.ltfi.fr/when-the-fix-doesnt-fix-it#takeaways" class="hash-link" aria-label="Direct link to Takeaways" title="Direct link to Takeaways" translate="no">​</a></h2>
<ul>
<li class="">If a reverse-proxied request to a router/appliance admin UI gets silently reset, check the <code>Host</code> header first — a lot of consumer routers guard against exactly this.</li>
<li class="">When a <em>fix</em> 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.</li>
<li class="">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.</li>
</ul>]]></content:encoded>
            <category>Homelab</category>
            <category>Networking</category>
            <category>nginx</category>
            <category>Debugging</category>
        </item>
    </channel>
</rss>