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
AandAAAArecords) 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、验证码或访问控制。
按最小改动修复并验收
- 确认 AAAA 的目标确实是承载该站点的 IPv6 入口,检查安全组、路由、80 端口监听和 IPv6 虚拟主机。
- 在 IPv4、IPv6 的每个入口部署同一 token 文件,确认权限、根目录和反代路径一致。
- 用
curl -4、curl -6比较状态码、跳转链和正文;不要只看浏览器首页。 - 若使用多节点或 CDN,确认挑战路径不会被缓存成旧 token,也不会被 WAF 的验证码页替换。
- 只有业务明确不支持 IPv6 并完成影响评估后,才移除 AAAA;DNS 变更后等待 TTL,再复核解析结果。
修复后让 ACME 客户端按正常流程重试即可。不要反复提交新订单碰运气,也不要公开账户密钥、内网拓扑或业务域名日志。
最后的故障清单
- 有 AAAA 时,已明确验证方初始连接优先 IPv6。
- 已区分"IPv6 连接超时"与"IPv6 返回错误内容"。前者特定条件下可回退,后者不会靠 IPv4 救场。
- A/AAAA 指向、80 端口、虚拟主机、挑战文件和多节点内容已逐项对比。
- 重定向不超过 10 层,只使用 HTTP/HTTPS 与 80/443;没有用 WAF/验证码绕过修复。
- 未无条件删除 AAAA,变更前已确认业务对 IPv6 的真实需求。
先看验证方实际走到的地址和拿到的正文,通常比重启服务更快定位。

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