Let’s Encrypt acme.sh 自动续期 SSL 证书实战

Let's Encrypt acme.sh 自动续期流程示意

本文教你如何用 acme.sh 一键签发 Let’s Encrypt SSL(Secure Sockets Layer,安全套接层,业内已统一为 TLS)证书并配置可靠的自动续期,覆盖单域名、多域名、通配符三类场景。读完你会得到三件产出:覆盖 webroot 与 DNS(Domain Name System,域名解析系统)API 两种验证方式的脚本模板;针对 Nginx 与 Apache 的部署 hook 与 reload 策略;针对续期失败、cron 漂移、证书撤销的踩坑清单。

一、Let’s Encrypt 与 acme.sh 的选型

Let’s Encrypt 是 ISRG 运营的免费证书机构,每张证书有效期 90 天,靠 ACME(Automatic Certificate Management Environment,证书自动化管理协议)签发。常见的 ACME 客户端有官方 certbot 与社区维护的 acme.sh:certbot 用 Python 写、依赖较重;acme.sh 用纯 Shell 写、零依赖且几乎所有 DNS 服务商的 API 都内置支持。中小型站点强烈推荐 acme.sh,部署一次就能管几十个域名。

场景快速判断:单台 VPS(Virtual Private Server,虚拟专用服务器)跑 1 到 3 个二级域名用 webroot 验证最简单;需要通配符或服务器在内网无法 80 端口验证时必须走 DNS-01;多机集群且证书要分发到多台时,建议把签发集中在一台并通过 rsync 推到其他节点。云服务器(基于虚拟化的弹性计算资源)的机型与带宽(Bandwidth,即网络出口数据传输速率,单位 Mbps)不影响签发本身,但会影响 HTTPS 握手性能,相关选型可参考 云服务器选购指南

二、安装 acme.sh 与首次签发

安装命令一行搞定,会在 ~/.acme.sh 下创建工作目录并自动注册一条 cron:

命令依次是:用 curl get.acme.sh | sh 安装并传入邮箱,source ~/.bashrc 让 alias 生效,最后用 --set-default-ca --server letsencrypt 把默认 CA 切到 Let’s Encrypt。默认 CA 是 ZeroSSL,必须显式切换才会签发我们要的证书。第一次签发推荐 webroot 方式,假设站点根目录是 /var/www/html:

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

webroot 模式会在 .well-known/acme-challenge/ 放一个临时校验文件,Let’s Encrypt 服务器访问该文件确认你对域名的控制权。前置代理或 CDN(Content Delivery Network,内容分发网络)一定要给这个路径单独开放,否则会出现 “Could not connect to host” 错误。如果站点通过 Nginx 反向代理对外,相关配置可参考 美国 VPS 部署教程 中的端口与防火墙章节,签发前先在边缘节点放行 ACME 路径,再触发签发。

三、通配符证书与 DNS API 验证

通配符 *.example.com 只能走 DNS-01 验证,签发时 acme.sh 会要求你在域名 TXT 记录里写一条 _acme-challenge.example.com 的随机值。手动签发可以接受一次,但对自动续期来说必须用 DNS API。acme.sh 内置了 Cloudflare、阿里云、DNSPod、AWS Route53 等数十家服务商的 API 实现。

以 Cloudflare 为例,先到控制台拿到 API Token,导出环境变量后调用 acme.sh --issue --dns dns_cf -d example.com -d '*.example.com'

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

首次签发后凭据会被写入 ~/.acme.sh/account.conf,续期时自动读取,不必每次重导。安全性建议:把 Global Key 换成范围更小的 API Token,权限只给 Zone:DNS:Edit,限定到对应 zone,这样即便泄露影响也可控。

CDN 用户额外注意:Cloudflare 橙云开启后 Let’s Encrypt 仍能正常验证 DNS-01;如果你用了 Cloudflare 边缘证书,源站证书只在 Full strict 模式下被回源校验,相关策略可参考 W3 Total Cache 调优实战WordPress 香港主机选购指南

四、自动续期与部署 hook

acme.sh 自动写入的 cron 每天凌晨执行一次 acme.sh --cron,检查证书剩余天数,距离过期 ≤30 天时自动续期。配合 --reloadcmd 可在续期后 reload Nginx 或 Apache 让新证书立即生效。

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

--install-cert 只需执行一次,acme.sh 会把目标路径与 reload 命令记录到内部配置,后续续期会自动复制到同样位置并触发 reload。Apache 用户把 reloadcmd 换成 systemctl reload apache2 即可,路径相应调整到 Apache 的证书目录。这套机制的一个隐藏好处是配置与脚本完全解耦,运维只需关注证书目录与 reload 命令两件事。

建议把 cron 时间从默认 0:00 错峰到 3:30 左右,避开很多站点凌晨备份的高峰;同时增加一行邮件或机器人通知,续期失败时第一时间感知。具体做法是用 crontab -l 把已有 acme.sh 行剔除后追加一条新的:分钟与小时分别写 30 与 3,命令指向 /root/.acme.sh/acme.sh --cron --home /root/.acme.sh,日志重定向到 /var/log/acme.log。这样每天凌晨三点半固定执行一次,日志独立留存便于追溯。

五、多域名与多机部署

单台机器跑十几个域名时,建议每个站点单独签发并各自配置 --reloadcmd,避免一张 SAN 证书内任一域名出错连累全部。命令模板:

用 for 循环逐个签发即可:循环里先 acme.sh --issue-d $d -w /var/www/$d/html,再 --install-cert 把 key 与 fullchain 复制到 nginx 的 ssl 目录,最后挂上 reloadcmd。

多机集群建议把签发集中到一台”证书机”,续期后用 rsync over SSH 推到业务节点再 reload。--reloadcmd 改成调一段同步脚本即可。证书机本身不对外提供服务、只跑 acme.sh 与 rsync。私钥统一设 0600 权限并用 chown 单独划给运维账号,是最低限度的硬化。

六、常见故障排查

本节给出一组生产里最常踩的高频问题与处理方向。Could not connect to host 通常是 webroot 路径未开放或防火墙拦截,先用 curl 访问 .well-known/acme-challenge/test 自测;DNS-01 验证失败多数是 API 凭据过期或权限不足,重新生成 Token 即可;签发提示 too many certificates already issued 是触发了 Let’s Encrypt 同一注册域每周 50 张的限速,可切到 staging 环境测试;续期 cron 不执行通常是 crontab 路径或 SHELL 变量缺失,把命令绝对路径写全并显式设置 PATH。证书撤销执行 acme.sh --revoke 后必须紧跟 --remove,否则下次续期会原地踏步报错。更多 HTTPS 与服务器安全议题可继续阅读 WordPress 分类下的相关文章

总结与建议

Let’s Encrypt 配合 acme.sh 是中小站点 HTTPS 化的事实标准:免费、自动、跨平台。建议你先在测试机走通 webroot 签发、DNS API 通配符、cron 自动续期与 reload hook 四个动作,再推广到生产环境。下一步可考虑把 acme.sh 与 Vault 或 1Password 这类凭据管理集成;规模再大就上 step-ca 自建 CA 与 Let’s Encrypt 并行,内网走自签、对外走公共证书。最后把所有命令与故障案例沉淀到一份 runbook,凌晨告警的概率会显著下降,团队整体的运维成熟度也会上一个台阶。

发表评论