Caddy ECH 实操指南:把访客的访问域名藏进加密信封
2026/8/29 14:14:30 网站建设 项目流程

Caddy ECH 实操指南:把访客的访问域名藏进加密信封

【免费下载链接】caddyFast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS项目地址: https://gitcode.com/GitHub_Trending/ca/caddy

上次线上抓包时我发现一件扎心的事:流量明明是 HTTPS,SNI 字段却是明文的——链路上任何 observer 都能看到你访客正在访问哪个站点。Caddy 的 ECH(Encrypted Client Hello)功能加密的正是这个暴露面。

它到底在解决什么:信封上的收件人地址

ECH 解决的问题只有一个:传统 TLS 握手里,客户端发出的第一个数据包(ClientHello)带着明文的域名(SNI),观察者偷看一眼就知道"谁在访问哪个站"。这个口子比想象的大:行业调研显示约92%的 Web 流量仍把 SNI 明文暴露。

最直观的理解是寄信。邮递员必须看到信封上的地址才能投递,传统握手就是一层信封,地址(SNI)大大咧咧写在外面。ECH 把它换成双层信封:

  • 内层信封:写真实域名,叫inner ClientHello
  • 外层信封:写一个通用的"公共名称"(public name),叫outer ClientHello

外层信封随 DNS 记录公布一份由 X25519 生成的公钥,客户端用它把内层信封加密后塞进外层;只有服务器能用私钥拆开外层、取出真实域名。邮递员(网络观察者)自始至终只看到那个公共名称。

这条路走了好几年,演进如下:

时间名称特点局限
2018ESNI(RFC 8744)只加密 SNI 字段浏览器支持撤掉后已废弃
2019–2022ECH 草案(draft-ietf-tls-esni)加密整个 ClientHello依赖特定 DNS 记录,部署复杂
2023+ECH + RFC 9460(HTTPS/SVCB 记录)发布机制标准化,浏览器默认支持客户端需开启 DoH/DoT 才生效

Caddy 的实现位于modules/caddytls/ech.go,通过 TLS app 的encrypted_client_hello字段暴露,密钥的生成、存储、轮换全在 Caddy 内部自动完成,不需要你手写任何密钥。

⚡ 一句话记住:邮递员只能看到外层信封的地址;ECH 加密的是整个 ClientHello,而不只是 SNI。

从零跑起来:3 步让 ECH 上线

你只需要两样东西:一个自己控制的"外层信封"域名,和一个能写记录的 DNS 服务商。

✅ 动手前自检清单:

  • ✅ Caddy ≥ 2.8.0(该版本开始支持 ECH)
  • ✅ 有一个可解析的域名,且 A 记录已指向这台服务器
  • ✅ 手里有能写记录的 DNS 服务商 API(如 Cloudflare),且域名 DNS 由它托管
  • 🔧 第三方 DNS provider 不在官方构建里,需先用 xcaddy 构建:
xcaddy build --with github.com/caddyserver/dns-providers

最小可运行 Caddyfile:

{ # 声明"外层信封"公共名称;密钥由 Caddy 自动生成并存储 ech public.example.com { dns cloudflare { # 用来发布 HTTPS 记录的 DNS 服务商 api_token {env.CF_API_TOKEN} } } } # 真实业务域名:观察者只会看到 public.example.com api.example.com, blog.example.com { reverse_proxy 127.0.0.1:8080 }

caddy run之后,Caddy 替你干完三件事:生成 X25519 密钥对、为公共名称申请证书、把 ECH 配置写进每个受保护域名的 HTTPS 记录。

两条配置建议:

  1. 公共名称越少越好,通常一个就够。Caddy 源码注释里明确建议这么做:多个域名共用同一个外层名称,匿名集最大,流量分析最难。
  2. 公共名称选一个与业务无关的中性域名(自己买块"停车域名"即可),它的 DNS 也必须指向这台服务器——否则证书发不下来,客户端会被迫退回明文暴露真实域名,隐私反而更差。

⚡ 一句话记住:全局块里一行ech,出键、发证书、发记录全自动;公共名称要"少、中性、能解析"。

验证与排坑:3 层验证 + 3 个高频报错

按"本地 → 远程 → 浏览器"三层验证,全过了才算 ECH 真正生效。

📋 本地层:确认配置与密钥

caddy adapt --config Caddyfile # JSON 中应出现 encrypted_client_hello

日志里看到generated new ECH config说明密钥已生成。

🌐 远程层:确认 DNS 发布结果

dig +short -t HTTPS api.example.com # 输出里应能看到 ech= 参数

🖥 浏览器层:打开chrome://net-internals/#ech,访问一次站点,可以看到握手实际使用的外层名称。

三个高频问题,都是"现象 → 原因 → 解法":

  1. 现象:日志出现domain does not have any existing records, so skipping publication原因:该域名原本没有任何 DNS 记录,直接加 HTTPS 记录会破坏通配 A 记录的解析。解法:先确保域名已有 A/AAAA 记录。
  2. 现象:日志出现domain has CNAME record, so unable to publish原因:CNAME 与 HTTPS 记录互斥,无法共存。解法:把 CNAME 改成 A 记录再重启 Caddy。
  3. 现象:浏览器里仍是明文 SNI。原因:客户端没开 DoH/DoT,拿不到记录;或公共名称证书没发下来。解法:浏览器开启 DoH,并检查公共名称的 DNS 解析与 ACME 验证是否通过。

⚡ 一句话记住:服务器发布成功 + 客户端 DoH 开启,缺一不可,否则 ECH 会静默退回明文 SNI。

值不值得上

先给结论:开销小、维护成本几乎为零,但功能仍属实验阶段。数字说话:

  • 性能开销:每次握手多一次 HPKE 解密,增加<1ms,体感为零;
  • 维护成本30 天自动轮换密钥,90 天后自动清理过期密钥,全程零手工;
  • 协议代价:ECH 强制TLS 1.3,Caddy 会自动把连接最低版本抬上去;
  • 兼容度:Chrome、Edge、Firefox 均已默认开启 ECH,覆盖约90%的浏览器流量,但客户端需启用 DoH/DoT 才生效。

一个诚实的提醒:源码里该功能标注着EXPERIMENTAL,配置结构未来可能调整;当前发布的记录 TTL 只有5 分钟,是现阶段的临时值。

适合:面向公网、在意"用户—域名"关联被分析,或域名本身敏感、属于隐私敏感行业的站点。不适合:纯内网服务,或 DNS 托管在无法写 HTTPS 记录的厂商上。

下次抓包时,你看到的明文 SNI 终于可以装进信封里了。建议先拿一个测试域名试水:配一行ech,发布后用一条dig验收。

【免费下载链接】caddyFast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS项目地址: https://gitcode.com/GitHub_Trending/ca/caddy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询