单公网IP极限操作:HAProxy+Caddy搭建立体网络架构实录
手里只有一个公网 IP,却想把 Web 服务、博客、NAS 访问全都挂上去,还想顺便搭一个 Tailscale DERP 节点加速组网?这听起来像是在“螺蛳壳里做道场”,但只要网络拓扑设计得当,这一切完全可以优雅共存。
最近折腾了一套 HAProxy + Caddy + DERP 的组合拳,配置过程虽然踩了不少坑,但搞定后的成就感爆棚。这套方案的核心在于“极限复用端口”,让同一个 IP 和端口(主要是 443 端口)能够同时服务于多种不同的网络流量。
今天就来复盘一下这套架构的设计思路和技术细节,如果你也在为单 IP 的服务部署发愁,希望这篇笔记能给你一些启发。
为什么这么麻烦?
在常规的三层架构(Nginx/Caddy -> Docker)中,443 端口通常被 Web 服务器独占。如果你还想跑 DERP(Tailscale 的中继节点),因为它也强依赖 443 端口,两者就会发生冲突——毕竟你的公网 IP 只有一个,443 端口也只有一个。
解决办法通常有两个:一是加 IP,二是换端口。但加 IP 意味着额外的成本,换端口(比如 DERP 跑在 8443)又不够优雅,且在某些被严格封禁的网络环境下可能无法连接。
为了在单 IP 上实现“既要又要”,我们引入了 HAProxy 作为系统的流量总闸,通过四层 SNI 嗅探来分发 Traffic。
HAProxy 通过四层 SNI 路由分发不同网络流量的拓扑示意图
核心架构:L4 SNI 路由
这套方案的灵魂在于 HAProxy 的 mode tcp 模式。 HAProxy 工作在 OSI 四层,它并不解密流量,也不破坏证书链,而是在 TLS 握手阶段通过 ClientHello 报文提取 SNI(Server Name Indication)字段。
简单来说,就是 HAProxy 站在门口看一眼来访者说要找谁(SNI),然后把你领到对应的房间:
- 如果你在找 DERP 节点的域名(比如
derp.example.com),HAProxy 就把流量转发给 DERP 服务(监听 8443 端口); - 如果你在找普通的网站(比如
blog.example.com),HAProxy 就把流量转交给 Caddy(监听 8444 端口)。
流量拓扑全解析
为了方便大家理解,我把整个路由过程拆解成了三条清晰的线路,这也是未来排查网络故障时的“作战地图”。
1. 证书申请与续期(Port 80 HTTP)
这是最基础的一环。Caddy 自动申请 Let's Encrypt 证书需要用到 80 端口进行验证。
流向数据包: 外部请求 (Let's Encrypt) ➔ *HAProxy (监听 :80) ➔ 转发给
caddy_http后端 ➔ Caddy 容器 (127.0.0.1:8080) ➔ 完成验证并落地证书。
这一步只需要简单的 HTTP 转发,没有太多花哨的操作,保证了 HTTPS 服务的生命周期管理。
2. Tailscale 隧道中转(Port 443 加密透传)
这是这套架构的高光时刻。DERP 协议虽然跑在 443,但它的流量特征和普通 Web 略有不同,我们通过特定的 SNI 域名来识别它。
流向数据包: Tailscale 客户端 ➔ *HAProxy (监听 :443, TCP 模式) ➔ 嗅探到 SNI 为 DERP 域名 ➔ 发送给
derp_backend➔ DERP 容器 (127.0.0.1:8443) ➔ 建立连接,开始中继流量。
这里的关键是 tcp-request inspect-delay 和 req.ssl_hello_type 1 配置,它们告诉 HAProxy 稍微等一下,看看 SSL 握手包里的 SNI 是什么,再决定往哪转。
3. 普通网站服务(Port 443 代理拆包)
对于常规的 Web 服务(如博客、网盘),我们需要转发给 Caddy 处理。但这里有个坑:由于 HAProxy 在四层转发,Caddy 拿到的流量源 IP 会是 HAProxy 的 IP(也就是 127.0.0.1),这会导致日志记录错误和 GeoIP 功能失效。
为了解决这个问题,我们引入了 Proxy Protocol v2。
流向数据包: 浏览器/用户 ➔ *HAProxy (监听 :443, TCP 模式) ➔ 嗅探 SNI 为普通域名 ➔ 发送给
caddy_https后端 (附加 Proxy Protocol 头) ➔ Caddy 容器 (127.0.0.1:8444) ➔ Caddy 解析 PP 头获取真实用户 IP ➔ 反向代理至下游容器。
这一步确保了你的应用层能真实看到来访者的 IP 地址,对于做访问统计或安全阻断非常重要。
这样折腾到底值不值?
说实话,配置这套架构的过程是“连环坑”。HAProxy 的配置语法比较晦涩,SNI 嗅探的参数调优、Caddy 接收 Proxy Protocol 的配置,每一步都需要仔细调试。一旦某个环节写错,所有 443 端口的服务都要瘫痪。
但是,当你在单节点上实现 Web 服务、Tailscale 组网、SSL 自动化管理的完美融合时,那种对网络流量“随意操控”的掌控感是非常极致的。这不仅是省了几块钱公网 IP 的事,更是对网络编排能力的一次实战演练。
避坑小贴士
如果你打算复刻这套方案,有几个细节千万注意:
- SNI 冲突:确保 DERP 的域名和你的 Web 服务域名完全不同,HAProxy 是靠域名做“路标”的,如果域名搞混了,流量就会串门。
- Proxy Protocol 开关:Caddy 必须显式配置接收
proxy_protocol,否则它会因为收到不认识的头部而直接断开连接,导致 502 错误。 - Docker 网络模式:如果后端服务都在 Docker 里,建议使用 host 模式或者小心处理 bridge 网络的 IP 映射,不然 HAProxy 找不到 127.0.0.1 的后端。
折腾技术嘛,不就是为了把不可能变成可能吗?希望这篇梳理能帮你少走几步弯路。

评论已关闭