Caddy ECH 配置教程:两步隐藏 TLS 握手里的真实域名
【免费下载链接】caddyFast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS项目地址: https://gitcode.com/GitHub_Trending/ca/caddy
读完这篇,你能给自己的 Caddy 站点开启 ECH(Encrypted Client Hello,加密客户端问候),让握手阶段暴露的服务器名称变成你指定的公共名称。
你的访问域名正在明文传输
把任意一次 HTTPS 连接抓包看,最扎眼的一帧就是 ClientHello(TLS 握手的第一条客户端消息)。它里面夹着一个 SNI(Server Name Indication,服务器名称指示)字段,值就是用户正在访问的域名——明文,任何中间节点都能读。
你的网站内容加密得好好的,域名本身却像没贴封条的包裹标签,谁都能看。运营商、公共 Wi-Fi 节点、公司网关,全都顺理成章地知道"这台设备正在访问 example.com"。
Caddy 在 TLS 应用里内置了 ECH 支持(实现位于 modules/caddytls/ech.go),能自动帮你生成密钥、轮转密钥,并把配置发布到 DNS。注意:该模块在代码里标注为 EXPERIMENTAL(实验性),接口可能随版本调整。
原理拆解:一次"共享快递柜"式的握手
打个比方:你把包裹寄到共享快递柜,面单只写"中心快递柜"这个地址(公共名称),自己取件的格口号通过加密短信单独发给你。邻居只能看到有人频繁往同一个快递柜寄包裹,猜不出里面是谁寄给谁的。
具体到 Caddy 的工作机制:
- 生成密钥对:首次启动时,Caddy 用 DHKEM(X25519, HKDF-SHA256) 算法自动生成一对 HPKE 密钥(HPKE 是基于混合公钥的封装加密,负责把对称密钥安全地交给对方),连同公共名称一起打包成 ECHConfig 存进本地存储。
- 发布到 DNS:配置了 DNS 提供商后,Caddy 把你保护域名的 HTTPS 记录(RFC 9460 定义的 DNS 记录类型)写入该域名,ECHConfig 以 base64 形式放在
ech参数里。 - 客户端封装请求:支持 ECH 的浏览器在解析域名时读到这条记录,把真实 ClientHello(含真实 SNI)加密后,套一层使用公共名称的外层 ClientHello 发出去。
- 服务端拆包:外层用公共名称完成证书验证,Caddy 再用私钥解开内层,按真实域名路由到对应站点。整个机制要求 TLS 1.3 及以上,Caddy 会对启用 ECH 的连接策略自动抬高最低版本。
兼容性检查清单
动手前先确认四件事:
- 🧱 Caddy 版本包含 ECH 模块(当前仓库实现见 modules/caddytls/ech.go),且你的 Caddyfile 解析走的是 caddyconfig/httpcaddyfile/options.go 中的
ech全局选项 - 🌐 你的域名支持写入 HTTPS 记录,且 DNS 服务商提供可写入的 API(Caddy 需要带 DNS 提供商模块的自定义构建)
- 📜 公共名称(public_name)的解析已指向这台服务器,Caddy 要给它签发证书
- 🧪 客户端侧:浏览器较新版本 + 启用了 DoH(DNS over HTTPS)或 DoT,否则 DNS 里的 ECH 配置读不到
最小可运行配置
Caddyfile 里只加一个ech全局选项:行内写公共名称,块内用dns子指令指定发布用的 DNS 提供商。
{ ech ech.example.com { dns cloudflare { api_token $CLOUDFLARE_TOKEN } } } https://shop.example.com { respond "hi" 200 }保存后跑caddy reload --config Caddyfile。Caddy 会自动为ech.example.com申请证书(代码里已把公共名称加入自动化策略,见 caddyconfig/httpcaddyfile/tlsapp.go),并为shop.example.com写入 HTTPS 记录。
ECH 验证三步
dig +short HTTPS shop.example.com curl --ech https://shop.example.com -I- 第一条应返回带
ech=参数的记录;没有就先检查 DNS 提供商配置 - 第二条用 curl 以 ECH 模式握手,能正常返回响应头即服务端链路通
- 第三步:Chrome 里打开
chrome://net-internals/#ech,确认该域名出现在已加载的 ECH 配置列表中,抓包时 SNI 应显示为公共名称
避坑指南:三个高频故障
现象:日志出现 "domain has CNAME record",站点始终走明文 SNI。原因:HTTPS 记录不能与 CNAME 共存,Caddy 检测到 CNAME 会直接跳过发布。解法:把该域名的 CNAME 换成 A/AAAA 记录后重新加载配置。
现象:域名下没有任何 DNS 记录,只靠通配符 A 记录兜底,发布日志提示 "does not have any existing records"。原因:给一个没有任何记录的域名写 HTTPS 记录,会让通配符解析对它失效,Caddy 为避免打断解析主动跳过。解法:先给域名加一条真实的 A/AAAA 记录,再让 Caddy 发布。
现象:客户端偶尔退回明文 SNI 握手。原因:公共名称证书没签下来(域名没解析到本服务器),客户端拿不到可用的 ECH 配置,只能裸连。解法:确认ech.example.com的解析指向本机且证书状态正常;另外用了 On-Demand TLS(按需签证书)的域名,要在publication.domains里显式列出,否则首次连接仍会暴露真实域名。
一页收束:开启 ECH 前后的差别
| 对比项 | 普通 TLS | 开启 Caddy ECH |
|---|---|---|
| 中间节点看到的 SNI | 真实域名 | 公共名称 |
| 握手最低版本 | 取决于配置 | 强制 TLS 1.3 |
| 密钥管理 | 无 | 自动生成,约 30 天轮转、90 天后清理 |
| 配置分发 | 无 | 自动写入 DNS HTTPS 记录 |
现在就去改你的 Caddyfile:加一段ech选项,reload,然后跑上面两条验证命令,看 SNI 是否已变成公共名称。
【免费下载链接】caddyFast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS项目地址: https://gitcode.com/GitHub_Trending/ca/caddy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考