Hermes Dashboard 反代访问异常排障复盘

Hermes Dashboard 反代访问异常排障复盘

有些运维问题最麻烦的地方,不是它完全不会报错,而是每一层看起来都对,偏偏合在一起就不工作。

这次我们做 Hermes Dashboard 的域名访问时,遇到的就是这样一个问题。

一开始大家都以为,这无非就是“把服务挂到域名下面”而已:

域名 → 反向代理 → 本机服务

听上去像一条很普通的链路,但真正开始之后,事情比想象中绕得多。目标也很明确:服务留在内网,外部只走统一入口,不把管理端口直接暴露出去。

一开始:服务其实是好的

Hermes Dashboard 本身并没有问题。
我们先确认了它的监听状态,某个本地端口确实起来了,服务也能正常响应请求。

后来为了安全起见,我们把它从对外监听收回到本机:

1
hermes dashboard --host 127.0.0.1 --no-open

这样做的好处是:

  • 本地管理端口不再直接暴露公网
  • 只能由本机反向代理访问
  • Dashboard 更符合生产环境的安全要求

真正适合生产的方式,应该是让它只在本机监听,然后由统一的反向代理层做代理。

第二层:反向代理看起来也没错

某个面板管理下的站点配置如下:

  • /path/to/site.conf
  • /path/to/proxy.conf

核心反代逻辑是:

1
proxy_pass http://127.0.0.1:PORT;

也就是说,外部请求应该经过:

1
域名 -> 反向代理 -> localhost:PORT -> Hermes Dashboard

从本机回源测试看,这条链路是通的。

为了把入口理顺,我们还补了这些东西:

  • 80 -> 301 https
  • 443 -> 反代到本地服务
  • 一度加过 basic auth
  • 后来又把 basic auth 去掉
  • 再后来又重新加回去做登录保护
  • 最后又再次关掉,确认问题不在认证层

一路排下来,你会发现:OpenResty 本身并不是坏的,反代也不是坏的。

真正让人迷惑的,是“浏览器报重定向次数太多”

最先暴露问题的,不是服务端日志,而是浏览器。

页面不是报 404,也不是 500,而是直接提示:

重定向次数太多

这类现象通常不是后端完全不可用,而是某一层在不断改写请求路径或协议,导致请求在不同端之间来回循环。我们很快意识到,问题已经不是“某个组件能不能跑”,而是“整条访问链路的语义是否一致”。

根因:Cloudflare 代理模式与源站 HTTPS 重定向冲突

1. 域名流量并不是直接打到源站

我们检查 DNS 后发现,example.yourdomain.com 解析出来的是 Cloudflare 的边缘节点地址,而不是源站机器 IP。

这意味着实际链路是:

1
浏览器 -> Cloudflare -> 源站

而不是直接访问服务器。

2. 源站本身会把 HTTP 重定向到 HTTPS

在站点反代配置里,我们配置了:

  • 80 -> 301 https
  • 443 -> 反代到本地服务

这是一种很常见、也很合理的生产策略:所有明文 HTTP 请求统一升级到 HTTPS。

3. Cloudflare Flexible 模式会让语义发生冲突

如果 Cloudflare 使用的是 Flexible 模式,那么它的行为是:

  • 用户访问 Cloudflare:HTTPS
  • Cloudflare 到源站:HTTP

这时候就出现了一个非常关键的问题:

源站视角

源站看到的是 HTTP 请求,于是按照配置把它重定向到 HTTPS。

Cloudflare 视角

Cloudflare 仍然坚持用 HTTP 回源。

于是双方开始互相“纠正”:

  1. 浏览器访问 https://example.yourdomain.com
  2. 请求进入 Cloudflare
  3. Cloudflare 以 HTTP 回源到源站
  4. 源站把 HTTP 重定向到 HTTPS
  5. Cloudflare 再按自己的策略回源
  6. 浏览器最终陷入 重定向循环

这就是“重定向次数太多”的根本原因。

为什么关闭代理模式后问题就好了

当我们把代理模式关闭,也就是改成 DNS only 后,域名访问立刻恢复正常。

这说明:

  • Hermes Dashboard 是正常的
  • 反向代理是正常的
  • basic auth 不是根因
  • 真正的问题在代理层与源站 HTTPS 策略的冲突

也就是说,问题不是“服务没起来”,而是“外层代理和源站对协议语义理解不一致”。

为什么推荐 Full (strict)

如果未来需要重新打开代理模式,正确的方式不是 Flexible,而是:

  • Full
  • 更推荐 Full (strict)

Full (strict) 的含义

它表示:

  • 浏览器到 Cloudflare:HTTPS
  • Cloudflare 到源站:HTTPS
  • Cloudflare 会验证源站证书是否有效

为什么它适合这个场景

因为我们的源站本来就希望:

  • HTTP 自动升级到 HTTPS
  • 正式入口走 HTTPS

如果 Cloudflare 回源也使用 HTTPS,那么双方语义一致,重定向循环就不会再发生。

换句话说:

  • Flexible:Cloudflare 用 HTTP 回源,容易和源站的 HTTP→HTTPS 冲突
  • Full (strict):Cloudflare 也用 HTTPS 回源,和源站策略一致

所以在这种架构中,Full (strict) 是更合理的生产选择

最后留下的做法

这次排障结束后,我们把比较稳妥的做法保留了下来:

1. 服务只监听本机

1
hermes dashboard --host 127.0.0.1 --no-open

2. 统一入口放在反向代理层

  • 80 -> 443
  • 443 -> 127.0.0.1:PORT

3. 认证按场景决定是否保留

  • 如果要保留认证,可以作为额外安全层
  • 如果正在排障,最好先去掉认证,避免干扰判断

4. 代理模式要么关闭,要么用 Full (strict)

  • 排障阶段:优先 DNS only
  • 生产阶段:如果要用代理,建议 Full (strict)

经验总结

这次问题最重要的经验,不是某一行配置,而是对整条链路的理解。

1. 单点没问题,不代表整体没问题

服务能起来、证书能过、入口也能通,并不意味着整体链路一定正常。
代理层、回源层、重定向策略叠加起来,才是最终用户看到的行为。

2. 代理层并不总是透明的

一旦开启代理模式,它不只是一个“转发器”,还会参与:

  • SSL 终止
  • 回源协议
  • 重定向行为
  • 缓存与规则处理

3. Flexible 和强制 HTTPS 的源站容易冲突

只要源站坚持 HTTP -> HTTPS,用 HTTP 回源就很容易出现重定向循环。

4. Full (strict) 才是更一致的做法

它保证了外层代理和源站在协议上是统一的,不会互相打架。

结论

这次 Hermes Dashboard 访问异常的根因,不是反代配置写错,也不是 Dashboard 服务本身异常,而是:

代理模式与源站的 HTTP→HTTPS 重定向策略发生了语义冲突,最终导致浏览器进入重定向循环。

当代理模式关闭后,问题立刻消失,说明源站链路本身没有问题。
如果未来要恢复代理,应该使用 Full (strict),让回源语义与源站策略保持一致。

使用 Hugo 构建
主题 StackJimmy 设计