<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Hermes on wallen</title><link>/tags/hermes/</link><description>Recent content in Hermes on wallen</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>自由阅读 任意分享</copyright><lastBuildDate>Sat, 09 May 2026 22:27:24 +0800</lastBuildDate><atom:link href="/tags/hermes/index.xml" rel="self" type="application/rss+xml"/><item><title>Hermes Dashboard 反代访问异常排障复盘</title><link>/p/hermes-dashboard-%E5%8F%8D%E4%BB%A3%E8%AE%BF%E9%97%AE%E5%BC%82%E5%B8%B8%E6%8E%92%E9%9A%9C%E5%A4%8D%E7%9B%98/</link><pubDate>Sat, 09 May 2026 22:27:24 +0800</pubDate><guid>/p/hermes-dashboard-%E5%8F%8D%E4%BB%A3%E8%AE%BF%E9%97%AE%E5%BC%82%E5%B8%B8%E6%8E%92%E9%9A%9C%E5%A4%8D%E7%9B%98/</guid><description>&lt;h1 id="hermes-dashboard-反代访问异常排障复盘">Hermes Dashboard 反代访问异常排障复盘
&lt;/h1>&lt;p>有些运维问题最麻烦的地方，不是它完全不会报错，而是每一层看起来都对，偏偏合在一起就不工作。&lt;/p>
&lt;p>这次我们做 Hermes Dashboard 的域名访问时，遇到的就是这样一个问题。&lt;/p>
&lt;p>一开始大家都以为，这无非就是“把服务挂到域名下面”而已：&lt;/p>
&lt;blockquote>
&lt;p>域名 → 反向代理 → 本机服务&lt;/p>
&lt;/blockquote>
&lt;p>听上去像一条很普通的链路，但真正开始之后，事情比想象中绕得多。目标也很明确：服务留在内网，外部只走统一入口，不把管理端口直接暴露出去。&lt;/p>
&lt;h2 id="一开始服务其实是好的">一开始：服务其实是好的
&lt;/h2>&lt;p>Hermes Dashboard 本身并没有问题。&lt;br>
我们先确认了它的监听状态，某个本地端口确实起来了，服务也能正常响应请求。&lt;/p>
&lt;p>后来为了安全起见，我们把它从对外监听收回到本机：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hermes dashboard --host 127.0.0.1 --no-open
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>这样做的好处是：&lt;/p>
&lt;ul>
&lt;li>本地管理端口不再直接暴露公网&lt;/li>
&lt;li>只能由本机反向代理访问&lt;/li>
&lt;li>Dashboard 更符合生产环境的安全要求&lt;/li>
&lt;/ul>
&lt;p>真正适合生产的方式，应该是让它只在本机监听，然后由统一的反向代理层做代理。&lt;/p>
&lt;h2 id="第二层反向代理看起来也没错">第二层：反向代理看起来也没错
&lt;/h2>&lt;p>某个面板管理下的站点配置如下：&lt;/p>
&lt;ul>
&lt;li>&lt;code>/path/to/site.conf&lt;/code>&lt;/li>
&lt;li>&lt;code>/path/to/proxy.conf&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>核心反代逻辑是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-nginx" data-lang="nginx">&lt;span class="line">&lt;span class="cl">&lt;span class="k">proxy_pass&lt;/span> &lt;span class="s">http://127.0.0.1:PORT&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>也就是说，外部请求应该经过：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">域名 -&amp;gt; 反向代理 -&amp;gt; localhost:PORT -&amp;gt; Hermes Dashboard
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>从本机回源测试看，这条链路是通的。&lt;/p>
&lt;p>为了把入口理顺，我们还补了这些东西：&lt;/p>
&lt;ul>
&lt;li>&lt;code>80 -&amp;gt; 301 https&lt;/code>&lt;/li>
&lt;li>&lt;code>443 -&amp;gt; 反代到本地服务&lt;/code>&lt;/li>
&lt;li>一度加过 basic auth&lt;/li>
&lt;li>后来又把 basic auth 去掉&lt;/li>
&lt;li>再后来又重新加回去做登录保护&lt;/li>
&lt;li>最后又再次关掉，确认问题不在认证层&lt;/li>
&lt;/ul>
&lt;p>一路排下来，你会发现：OpenResty 本身并不是坏的，反代也不是坏的。&lt;/p>
&lt;h2 id="真正让人迷惑的是浏览器报重定向次数太多">真正让人迷惑的，是“浏览器报重定向次数太多”
&lt;/h2>&lt;p>最先暴露问题的，不是服务端日志，而是浏览器。&lt;/p>
&lt;p>页面不是报 404，也不是 500，而是直接提示：&lt;/p>
&lt;blockquote>
&lt;p>重定向次数太多&lt;/p>
&lt;/blockquote>
&lt;p>这类现象通常不是后端完全不可用，而是某一层在不断改写请求路径或协议，导致请求在不同端之间来回循环。我们很快意识到，问题已经不是“某个组件能不能跑”，而是“整条访问链路的语义是否一致”。&lt;/p>
&lt;h2 id="根因cloudflare-代理模式与源站-https-重定向冲突">根因：Cloudflare 代理模式与源站 HTTPS 重定向冲突
&lt;/h2>&lt;h3 id="1-域名流量并不是直接打到源站">1. 域名流量并不是直接打到源站
&lt;/h3>&lt;p>我们检查 DNS 后发现，&lt;code>example.yourdomain.com&lt;/code> 解析出来的是 Cloudflare 的边缘节点地址，而不是源站机器 IP。&lt;/p>
&lt;p>这意味着实际链路是：&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">浏览器 -&amp;gt; Cloudflare -&amp;gt; 源站
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>而不是直接访问服务器。&lt;/p>
&lt;h3 id="2-源站本身会把-http-重定向到-https">2. 源站本身会把 HTTP 重定向到 HTTPS
&lt;/h3>&lt;p>在站点反代配置里，我们配置了：&lt;/p>
&lt;ul>
&lt;li>&lt;code>80 -&amp;gt; 301 https&lt;/code>&lt;/li>
&lt;li>&lt;code>443 -&amp;gt; 反代到本地服务&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>这是一种很常见、也很合理的生产策略：所有明文 HTTP 请求统一升级到 HTTPS。&lt;/p>
&lt;h3 id="3-cloudflare-flexible-模式会让语义发生冲突">3. Cloudflare Flexible 模式会让语义发生冲突
&lt;/h3>&lt;p>如果 Cloudflare 使用的是 &lt;strong>Flexible&lt;/strong> 模式，那么它的行为是：&lt;/p>
&lt;ul>
&lt;li>用户访问 Cloudflare：HTTPS&lt;/li>
&lt;li>Cloudflare 到源站：HTTP&lt;/li>
&lt;/ul>
&lt;p>这时候就出现了一个非常关键的问题：&lt;/p>
&lt;h4 id="源站视角">源站视角
&lt;/h4>&lt;p>源站看到的是 &lt;strong>HTTP 请求&lt;/strong>，于是按照配置把它重定向到 HTTPS。&lt;/p>
&lt;h4 id="cloudflare-视角">Cloudflare 视角
&lt;/h4>&lt;p>Cloudflare 仍然坚持用 HTTP 回源。&lt;/p>
&lt;p>于是双方开始互相“纠正”：&lt;/p>
&lt;ol>
&lt;li>浏览器访问 &lt;code>https://example.yourdomain.com&lt;/code>&lt;/li>
&lt;li>请求进入 Cloudflare&lt;/li>
&lt;li>Cloudflare 以 HTTP 回源到源站&lt;/li>
&lt;li>源站把 HTTP 重定向到 HTTPS&lt;/li>
&lt;li>Cloudflare 再按自己的策略回源&lt;/li>
&lt;li>浏览器最终陷入 &lt;strong>重定向循环&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>这就是“重定向次数太多”的根本原因。&lt;/p>
&lt;h2 id="为什么关闭代理模式后问题就好了">为什么关闭代理模式后问题就好了
&lt;/h2>&lt;p>当我们把代理模式关闭，也就是改成 &lt;strong>DNS only&lt;/strong> 后，域名访问立刻恢复正常。&lt;/p>
&lt;p>这说明：&lt;/p>
&lt;ul>
&lt;li>Hermes Dashboard 是正常的&lt;/li>
&lt;li>反向代理是正常的&lt;/li>
&lt;li>basic auth 不是根因&lt;/li>
&lt;li>真正的问题在代理层与源站 HTTPS 策略的冲突&lt;/li>
&lt;/ul>
&lt;p>也就是说，问题不是“服务没起来”，而是“外层代理和源站对协议语义理解不一致”。&lt;/p>
&lt;h2 id="为什么推荐-full-strict">为什么推荐 Full (strict)
&lt;/h2>&lt;p>如果未来需要重新打开代理模式，正确的方式不是 Flexible，而是：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Full&lt;/strong>&lt;/li>
&lt;li>更推荐 &lt;strong>Full (strict)&lt;/strong>&lt;/li>
&lt;/ul>
&lt;h3 id="full-strict-的含义">Full (strict) 的含义
&lt;/h3>&lt;p>它表示：&lt;/p>
&lt;ul>
&lt;li>浏览器到 Cloudflare：HTTPS&lt;/li>
&lt;li>Cloudflare 到源站：HTTPS&lt;/li>
&lt;li>Cloudflare 会验证源站证书是否有效&lt;/li>
&lt;/ul>
&lt;h3 id="为什么它适合这个场景">为什么它适合这个场景
&lt;/h3>&lt;p>因为我们的源站本来就希望：&lt;/p>
&lt;ul>
&lt;li>HTTP 自动升级到 HTTPS&lt;/li>
&lt;li>正式入口走 HTTPS&lt;/li>
&lt;/ul>
&lt;p>如果 Cloudflare 回源也使用 HTTPS，那么双方语义一致，重定向循环就不会再发生。&lt;/p>
&lt;p>换句话说：&lt;/p>
&lt;ul>
&lt;li>Flexible：Cloudflare 用 HTTP 回源，容易和源站的 HTTP→HTTPS 冲突&lt;/li>
&lt;li>Full (strict)：Cloudflare 也用 HTTPS 回源，和源站策略一致&lt;/li>
&lt;/ul>
&lt;p>所以在这种架构中，&lt;strong>Full (strict) 是更合理的生产选择&lt;/strong>。&lt;/p>
&lt;h2 id="最后留下的做法">最后留下的做法
&lt;/h2>&lt;p>这次排障结束后，我们把比较稳妥的做法保留了下来：&lt;/p>
&lt;h3 id="1-服务只监听本机">1. 服务只监听本机
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hermes dashboard --host 127.0.0.1 --no-open
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="2-统一入口放在反向代理层">2. 统一入口放在反向代理层
&lt;/h3>&lt;ul>
&lt;li>&lt;code>80 -&amp;gt; 443&lt;/code>&lt;/li>
&lt;li>&lt;code>443 -&amp;gt; 127.0.0.1:PORT&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="3-认证按场景决定是否保留">3. 认证按场景决定是否保留
&lt;/h3>&lt;ul>
&lt;li>如果要保留认证，可以作为额外安全层&lt;/li>
&lt;li>如果正在排障，最好先去掉认证，避免干扰判断&lt;/li>
&lt;/ul>
&lt;h3 id="4-代理模式要么关闭要么用-full-strict">4. 代理模式要么关闭，要么用 Full (strict)
&lt;/h3>&lt;ul>
&lt;li>排障阶段：优先 DNS only&lt;/li>
&lt;li>生产阶段：如果要用代理，建议 Full (strict)&lt;/li>
&lt;/ul>
&lt;h2 id="经验总结">经验总结
&lt;/h2>&lt;p>这次问题最重要的经验，不是某一行配置，而是对整条链路的理解。&lt;/p>
&lt;h3 id="1-单点没问题不代表整体没问题">1. 单点没问题，不代表整体没问题
&lt;/h3>&lt;p>服务能起来、证书能过、入口也能通，并不意味着整体链路一定正常。&lt;br>
代理层、回源层、重定向策略叠加起来，才是最终用户看到的行为。&lt;/p>
&lt;h3 id="2-代理层并不总是透明的">2. 代理层并不总是透明的
&lt;/h3>&lt;p>一旦开启代理模式，它不只是一个“转发器”，还会参与：&lt;/p>
&lt;ul>
&lt;li>SSL 终止&lt;/li>
&lt;li>回源协议&lt;/li>
&lt;li>重定向行为&lt;/li>
&lt;li>缓存与规则处理&lt;/li>
&lt;/ul>
&lt;h3 id="3-flexible-和强制-https-的源站容易冲突">3. Flexible 和强制 HTTPS 的源站容易冲突
&lt;/h3>&lt;p>只要源站坚持 &lt;code>HTTP -&amp;gt; HTTPS&lt;/code>，用 HTTP 回源就很容易出现重定向循环。&lt;/p>
&lt;h3 id="4-full-strict-才是更一致的做法">4. Full (strict) 才是更一致的做法
&lt;/h3>&lt;p>它保证了外层代理和源站在协议上是统一的，不会互相打架。&lt;/p>
&lt;h2 id="结论">结论
&lt;/h2>&lt;p>这次 Hermes Dashboard 访问异常的根因，不是反代配置写错，也不是 Dashboard 服务本身异常，而是：&lt;/p>
&lt;blockquote>
&lt;p>代理模式与源站的 HTTP→HTTPS 重定向策略发生了语义冲突，最终导致浏览器进入重定向循环。&lt;/p>
&lt;/blockquote>
&lt;p>当代理模式关闭后，问题立刻消失，说明源站链路本身没有问题。&lt;br>
如果未来要恢复代理，应该使用 &lt;strong>Full (strict)&lt;/strong>，让回源语义与源站策略保持一致。&lt;/p></description></item></channel></rss>