用 acme.sh 管泛域名证书的几种姿势

单域名证书用 HTTP-01 就够了,泛域名必须走 DNS-01:CA 要验证的是 _acme-challenge 这条 TXT 记录,跟 80 端口没关系。 好处是不用对外暴露 80,坏处是把 DNS API 的密钥交给了脚本。

装与初始化

curl https://get.acme.sh | sh -s email=admin@example.com
# 装完是个 cron:
crontab -l | grep acme
# 17 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

acme.sh 默认已经切到 ZeroSSL,想要 Let's Encrypt 就显式指定, 否则以后换 CA 会平白多一次重签。

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

DNS API 申请

以 Cloudflare 为例,密钥尽量用只对目标 zone 有 DNS 编辑权限的 token, 不要用全局 key:

export CF_Token="xxxxxxxx"
export CF_Account_ID="xxxxxxxx"
acme.sh --issue --dns dns_cf -d example.com -d '*.example.com'

签发成功后证书在 ~/.acme.sh/example.com_ecc/, 但不要直接让 nginx 读那里——续期时文件会被重写,路径虽然不变, 权限却是 600。正确做法是让 acme.sh 装到固定位置:

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

--reloadcmd 很关键,续期成功但没 reload,nginx 还在用旧证书, 你会以为续期失败了。openssl x509 -enddate 对比一下就知道。

几个常见的失败

报错原因
Invalid responseHTTP-01 时 80 被跳转到了 https,验证器跟不过去
DNS problem: NXDOMAINTXT 记录还没传播,或者写到了错误的 zone
too many certificates同一域名一周内重复签发,撞了速率限制,等一周或用 staging 调试

调试期强烈建议加 --staging,用测试 CA 跑通流程再换正式, 不然反复失败很容易把额度耗光。

reload 之外的自动化

如果证书不止 nginx 用(比如还给了邮件服务、或者某个 Go 服务自己读证书), 可以在 --reloadcmd 里串多个动作,写成一段 shell 就行。 我更倾向让所有消费者都读同一份 pem,reload 一次全部生效。

另外记得把 ~/.acme.sh 一起纳入备份。重建机器时把目录拷回去, 配合 --force 就能立刻恢复续期,不用等 DNS 传播。

一句话总结:证书路径要固定、reload 要挂上、调试先用 staging。
TLS acme.sh DNS