证书签发和续期

Verify error:acme.sh 域名验证失败的原因和修法

作者: · 最后核实

报错信息example.com:Verify error:Invalid response from http://example.com/.well-known/acme-challenge/...: 404

Verify error 的意思是:CA 去验证你的域名,失败了;acme.sh 只是把 CA 的解释原样打印在冒号后面。 关键看这段解释:HTTP 状态码(404、403)、超时、NXDOMAIN、Incorrect TXT record,各自对应不同的修法。

快速修复

定位问题最快的办法是在测试环境加调试参数重跑一次,失败不占正式额度:

acme.sh --issue -d example.com -w /var/www/html --staging --debug 2

再按冒号后面的内容查对应原因:

Verify error: 后面的内容 最可能的原因 看第几节
Invalid response ...: 404 网站目录或 server 块不对 1
Invalid response ...: 403 拒绝规则、WAF、权限 2
Timeout during connect、Connection refused 80 端口不通、IP 不对 3
Incorrect TXT record、NXDOMAIN looking up TXT DNS-01 记录查不到 4
CAA record ... prevents issuance CAA 没允许该 CA 5

staging 上成功后,去掉 --staging,加一次 --force 正式签发。

原因

Verify error 有 5 类原因:HTTP-01 的 404、403 和连接问题,DNS-01 记录查不到,以及 CAA 等 CA 端拒绝。最常见的是网站目录不对导致的 404。

1. HTTP-01 网站目录不对(404)

acme.sh 把验证文件写到 <网站目录>/.well-known/acme-challenge/。如果 Web 服务器给这个域名用的是另一个目录,CA 就会拿到 404。

  • 确认: echo ok > /var/www/html/.well-known/acme-challenge/t 后,curl http://example.com/.well-known/acme-challenge/t 返回 404。
  • 修复: -w 改成该 server 块实际使用的 root(用 nginx -T | grep root 查);或者改用 --nginx / --apache 模式,让 acme.sh 临时改配置。反向代理站点要加一个 location,让验证目录在本机直接返回。宝塔站点的根目录一般是 /www/wwwroot/<域名>。

2. 被拒绝访问(403)

  • 修复: 在禁止点开头文件的规则和 WAF 规则之前,给 /.well-known/acme-challenge/ 加例外,做法和 Certbot 403 unauthorized 完全一样。

3. 连接问题

  • 修复: 每一层防火墙(云安全组、系统防火墙、宝塔防火墙)都放行 TCP 80,删掉残留的 AAAA 记录,关闭“禁止海外访问”之类的地域屏蔽,见 Timeout during connect。--standalone 模式需要 80 端口空闲,提示缺少 socat 时先安装它。

4. DNS-01 记录查不到

DNS API 把记录写到了错误的区域、API 密钥权限不够,或者 CA 在所有权威服务器同步前就开始验证。

  • 确认: 运行过程中用 dig +short TXT _acme-challenge.example.com @权威服务器 查不到,或者查到的是旧值。
  • 修复: 检查 API 环境变量(DNSPod 用 DP_Id/DP_Key,阿里云解析用 Ali_Key/Ali_Secret,Cloudflare 用 CF_Token),确认域名的 NS 确实指向这家服务商;同步慢就加 --dnssleep 120。用 CNAME 委托验证时加 --challenge-alias。阿里云 RAM 子账号要授予云解析的管理权限。

5. CAA 等 CA 端拒绝

  • 修复: 在 CAA 里加上 CA 的标识,比如 letsencrypt.org,ZeroSSL 以其官方文档给出的为准;见 CAA record prevents issuance。

各环境修法

各环境的共同原则:acme.sh 数据目录要持久保存,证书用 --install-cert 部署,同一个域名只交给一个客户端签发。

切换 CA:

acme.sh --set-default-ca --server letsencrypt

安装证书(让续期后的证书自动部署到 Web 服务器):

acme.sh --install-cert -d example.com \
  --key-file /etc/nginx/ssl/example.com.key \
  --fullchain-file /etc/nginx/ssl/example.com.pem \
  --reloadcmd "systemctl reload nginx"

Docker(neilpang/acme.sh 镜像): 把 /acme.sh 数据目录挂成卷,账户和证书才能在重启后保留;否则每次运行都是新订单,容易触发速率限制。

通配符证书: 必须用 DNS-01(如 --dns dns_dp、--dns dns_ali),HTTP-01 无法验证 *.example.com。

升级: 老版本遇到 CA 接口变化会出各种错,先 acme.sh --upgrade。

宝塔面板: 宝塔的证书申请底层也是 ACME 客户端,面板里看到的“验证失败”提示和 Verify error 的后半句含义相同。面板申请和命令行 acme.sh 不要同时给同一个域名签发,否则两边互相覆盖证书文件,续期时间也会错乱。

不要这样做

下面 3 种做法会耗光签发额度、泄露密钥,或者让服务器失去防护。

  • 在定时任务里保留 --force。 每次都不管是否快到期就续,额度很快耗光。
  • 把 --debug 日志原样贴到论坛、群里。 里面有 API 密钥和账户地址。
  • 为了验证通过把防火墙或 WAF 全关掉。 只放行验证路径,或者改用 DNS-01。

常见问题

acme.sh 现在默认用的是哪家 CA?

从 3.0 版开始默认是 ZeroSSL,需要先用邮箱注册账户(acme.sh --register-account -m you@example.com)。执行 acme.sh --set-default-ca --server letsencrypt 改用 Let's Encrypt,或者每条命令加 --server letsencrypt。

怎么看到完整的请求和响应?

命令后面加 --debug 2(更详细用 --debug 3)。输出在终端里,开启日志时也会写进 ~/.acme.sh/acme.sh.log。发给别人看之前,先删掉里面的 API 密钥。

国内服务器装 acme.sh 很慢或者装不上怎么办?

从 acme.sh 在 Gitee 上的镜像仓库克隆后本地安装。官方安装脚本要从 GitHub 下载,国内网络可能很慢;安装后用 acme.sh --upgrade 保持更新。注意只从官方仓库或其官方镜像获取,不要用来路不明的修改版。

反复重试有什么影响?

每次验证失败都会计入 CA 的限制,Let's Encrypt 是每账户每标识符每小时最多 5 次。先用 --staging 调通,再去掉它并加一次 --force,替换掉测试证书。

资料来源

  1. acme.sh wiki
  2. acme.sh wiki: How to debug acme.sh
  3. Let's Encrypt: Challenge Types
  4. Let's Encrypt: Rate Limits