ACME HTTP-01 验证失败:AAAA 记录导致的 IPv6 排查

ACME 的 HTTP-01 验证失败时,很多人第一反应是重启 Nginx,第二反应是把防火墙规则全放开。若域名同时有 A 和 AAAA 记录,真正的入口可能在 IPv6:验证方初次连接会优先走 IPv6。IPv6 服务器能连上、却返回了错误内容时,IPv4 不会自动替你兜底。本文只围绕这个单一故障展开,使用 example.com 作为文档示例,不创建真实 ACME 订单。

先把故障现象分成两类

HTTP-01 的核心是:ACME 客户端把 token 文件放到 Web 服务器的 /.well-known/acme-challenge/ 路径,验证方从公网读取它。这里的"能打开网站"并不等于"能读到挑战文件":首页可能由 IPv4 站点提供,挑战路径却被 IPv6 上的另一台机器、默认虚拟主机或 WAF 接管。

排查时先问两个问题:域名是否存在 AAAA 记录;IPv6 到达的服务器是否返回了与 IPv4 完全相同的挑战内容。前者决定是否优先试 IPv6,后者决定"连通但内容错误"是否直接失败。

Let's Encrypt 为什么先走 IPv6

"When making outbound domain validation requests for a domain that has both IPv4 and IPv6 addresses (e.g. both A and AAAA records) Let's Encrypt will always prefer the IPv6 addresses for the initial connection."
------Let's Encrypt, IPv6 Support

这句话的关键是 initial connection:有 A 和 AAAA 时,第一次连接优先 IPv6。只有 IPv6 连接超时且有可用 IPv4 时,验证方才会尝试回退 IPv4;"回退"不是"IPv6 内容不对就改走 IPv4",也不是客户端可以配置的偏好开关。

所以不要看到 AAAA 就无条件删除。正确顺序是先把 AAAA 指向实际承载站点的 IPv6 地址,并让该入口提供同一个挑战文件;只有确认业务不打算支持 IPv6、且评估过移除记录的影响后,才考虑移除 AAAA。

用 dig 确认 A 与 AAAA 的去向

下面命令只读取 DNS。生产排查时把 example.com 换成自己控制的域名,并从多个递归解析器或网络位置复核;示例不会向 ACME 创建订单。

复制代码
dig +short A example.com
dig +short AAAA example.com
dig +short CNAME example.com

# 需要看权威链路时
 dig +trace AAAA example.com

若 AAAA 指向旧主机、停用的负载均衡或默认站点,优先修正 IPv6 入口。DNS 刚变更时记录解析结果和 TTL,不要用本机结果推断所有验证节点都已更新。

用 curl -4 和 curl -6 读取同一个测试文件

在实际受控站点先放一个无敏感信息的临时文件,例如内容为 http01-check-v1。下面用文档域名展示命令格式;它只是普通 HTTP 读取,不是申请证书。

复制代码
curl -4 -sS --max-time 10 -D - \
  http://example.com/.well-known/acme-challenge/http01-check

curl -6 -sS --max-time 10 -D - \
  http://example.com/.well-known/acme-challenge/http01-check

比较两次响应的状态码、Location、响应体和必要的缓存头。理想结果是两条路径都返回 200,正文完全一致;若 -6 超时,说明应检查 IPv6 路由、监听、ACL 和负载均衡,不要把"IPv4 能通"当作修复完成。若 -6 返回 200 但正文是默认页,问题是内容路由而不是连通性。

理解"会回退"与"不会回退"

"Our implementation of the HTTP-01 challenge follows redirects, up to 10 redirects deep."
------Let's Encrypt, Challenge Types

可把结果按下面方式判断:

IPv6 结果 验证方行为 排查方向
连接超时,IPv4 可用 初始请求可能回退 IPv4 修复 IPv6 网络,不能把回退当长期方案
连接成功,返回错误 token/默认页 不会因内容错误再回退 IPv4 修正 IPv6 虚拟主机、路径映射或同步文件
连接被拒绝、TLS/HTTP 其他错误 不属于"连接超时"回退条件 检查监听端口、ACL、代理和协议配置
IPv6 返回正确文件 进入后续验证流程 继续查缓存、多个节点和文件权限

尤其要注意:IPv6 站点"有响应"反而可能比超时更容易掩盖问题。它把错误页面完整地交给验证方,验证方会按错误响应判定挑战失败。

重定向的限制,别把它当万能补丁

Let's Encrypt 的 HTTP-01 说明明确:实现会跟随最多 10 层重定向;目标只接受 http:https:,端口只能是 80 或 443。跳到 HTTPS 时不会校验证书;HTTP-01 本身仍只能从 80 端口开始。

还有一个容易漏掉的 IPv6 细节:IPv6 到 IPv4 的回退只对验证 HTTP-01 的第一个请求提供,重定向后的请求不再获得同样的回退处理。比如 IPv6 首次连接超时,IPv4 返回了 HTTP 到 HTTPS 的跳转,后续 HTTPS 请求再次优先 IPv6 并超时,就可能失败。因此官方建议是修复 IPv6 配置,或仅对 ACME challenge 路径审慎处理 HTTP 到 HTTPS 的跳转;不要把全站改成不安全的明文,也不要为了验证绕过 WAF、验证码或访问控制。

按最小改动修复并验收

  1. 确认 AAAA 的目标确实是承载该站点的 IPv6 入口,检查安全组、路由、80 端口监听和 IPv6 虚拟主机。
  2. 在 IPv4、IPv6 的每个入口部署同一 token 文件,确认权限、根目录和反代路径一致。
  3. curl -4curl -6 比较状态码、跳转链和正文;不要只看浏览器首页。
  4. 若使用多节点或 CDN,确认挑战路径不会被缓存成旧 token,也不会被 WAF 的验证码页替换。
  5. 只有业务明确不支持 IPv6 并完成影响评估后,才移除 AAAA;DNS 变更后等待 TTL,再复核解析结果。

修复后让 ACME 客户端按正常流程重试即可。不要反复提交新订单碰运气,也不要公开账户密钥、内网拓扑或业务域名日志。

最后的故障清单

  • 有 AAAA 时,已明确验证方初始连接优先 IPv6。
  • 已区分"IPv6 连接超时"与"IPv6 返回错误内容"。前者特定条件下可回退,后者不会靠 IPv4 救场。
  • A/AAAA 指向、80 端口、虚拟主机、挑战文件和多节点内容已逐项对比。
  • 重定向不超过 10 层,只使用 HTTP/HTTPS 与 80/443;没有用 WAF/验证码绕过修复。
  • 未无条件删除 AAAA,变更前已确认业务对 IPv6 的真实需求。

先看验证方实际走到的地址和拿到的正文,通常比重启服务更快定位。

图:HTTP-01 的 IPv6 初始连接、有限回退与挑战内容核对路径

相关推荐
Lsetea2 小时前
ACME 报 rate limit:Let‘s Encrypt 限速类型与重试策略
运维·https·证书·ssl·acme
Lsetea1 天前
Nginx报SSL_CTX_use_PrivateKey failed:证书与私钥不匹配怎么排查
nginx·https·ssl证书·openssl·私钥
Lsetea3 天前
Cloudflare报526 Invalid SSL certificate:源站证书与Nginx回源TLS排查
nginx·https·ssl证书·cloudflare·tls
Lsetea3 天前
SSL证书快过期怎么自动检查:用OpenSSL checkend做7天预警与线上证书验收
https·ssl证书·openssl·systemd·证书监控
fareast_mzh7 天前
添加Kamatera的IPv6给主机
vps·ipv6·云主机
酒神dnspup11 天前
服务器端口检测怎么做?用 DNSPup 核对开放端口、监听服务与暴露风险
网络·网络协议·http·ssl证书·http状态码·服务器端口
酒神dnspup11 天前
SSL证书检测完整指南:用 DNSPup 排查过期、域名不匹配与 TLS 握手失败
网络·网络协议·http·ssl证书·http状态码
JZZC221 天前
39.IPv6——无状态地址自动配置
计算机网络·ipv6·无状态
JZZC221 天前
40.IPv6——有状态地址自动配置
计算机网络·ipv6·有状态