Let's Encrypt 证书自动化部署:Certbot 续期流程

很多站长的真实经历是这样的:第一年靠证书有效期一年"手工续一次",第二年用了 Let's Encrypt 后,却因为忘了续期导致网站突然出现"您的连接不是私密连接"。90 天短周期换来的免费证书,必须配上自动化才能省心。Let's Encrypt 证书默认有效期仅 90 天,因此"自动化续期"是生产环境能否长期稳定使用它的关键。

90 天不是拍脑袋定的:短有效期强制证书轮换,即使私钥泄露,影响窗口也被压缩到很小;配合自动化,短周期就不再是负担,反而成了一种安全设计。

官方文档强调,Certbot 的 renew 命令只续期临近到期的证书,可以安全地频繁运行(如每天),通常不会真正触发续期、也不会撞上 CA 速率限制。这意味着你可以放心地把续期交给定时任务。

一、安装 Certbot

主流 Linux 发行版都可以通过包管理器安装:

# Ubuntu/Debian
apt install certbot python3-certbot-nginx
# RHEL/CentOS
dnf install certbot python3-certbot-nginx

Certbot 2.0 起默认使用 ECDSA 私钥(secp256r1),也可用 --key-type rsa 显式指定。详细安装见Let's Encrypt 申请教程

二、认证方式选择

Certbot 通过 ACME 挑战证明你拥有域名,主要认证方式有三种:

插件 挑战类型 适用场景 备注
webroot HTTP-01 已有 Web 服务器 不停止服务,推荐
standalone HTTP-01 无 Web 服务器 需占用 80 端口
DNS 插件 DNS-01 通配符证书 需 DNS API 凭据

webroot 方式(不停止 Web 服务器,推荐给已有站点):

certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com

standalone 方式(需要占用 80 端口):

certbot certonly --standalone -d example.com

DNS 插件方式(唯一能签发通配符证书的方式,以 Cloudflare 为例):

pip install certbot-dns-cloudflare
certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d example.com -d '*.example.com'

如果需要一张证书覆盖多个子域名,把 -d 参数多写几个即可,例如 -d example.com -d shop.example.com -d blog.example.com,签发出来的是一张包含多个 SAN(Subject Alternative Name)的证书,站点之间可以共用同一份文件。

三、自动续期

certbot renew 会检查所有已安装证书并续期临近到期的。官方建议配合定时任务(cron 或 systemd timer)每天运行两次:

# 测试续期流程(不真正签发)
certbot renew --dry-run

# 查看已管理的证书
certbot certificates

Debian/Ubuntu 通过 apt 安装后通常已预置 systemd timer,自动运行 certbot renew。其他发行版可手动创建:

# /etc/systemd/system/certbot-renew.timer
[Unit]
Description=Run certbot renewal twice daily

[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=3600

[Install]
WantedBy=timers.target

创建后执行 systemctl enable --now certbot-renew.timer 并搭配对应的 service 即可。定时器里建议加一个 RandomizedDelaySec 分散执行时间,避免整点扎堆;而 --dry-run 演练也建议定时执行(例如每周一次),这样即使正式续期失败,也能提前在日志里暴露问题。

四、用 hooks 让续期真正生效

续期成功只是第一步,还要让 Web 服务器加载新证书。Certbot 提供三类 hooks:

  • --pre-hook:续期前执行
  • --post-hook:续期后执行
  • --deploy-hook仅在续期成功后执行(最常用)
certbot renew --deploy-hook "systemctl reload nginx"

对 Docker 部署,reload 命令要作用于容器,例如 docker compose exec nginx nginx -s reload。这也和Nginx 反向代理Docker Compose 生产部署衔接良好。

hooks 目录方式:把可执行脚本放进 /etc/letsencrypt/renewal-hooks/deploy/,Certbot 会在续期成功后按字母顺序自动运行它们,适合多个证书共用一个部署脚本。配合 ACME ARI 自动续期 这类新协议,续期体验会更顺滑。

五、证书文件与 Nginx 配置

签发后的证书位于 /etc/letsencrypt/live/<域名>/

  • fullchain.pem:服务器证书 + 中间证书,Nginx 的 ssl_certificate 用它
  • privkey.pem:私钥,必须保密,对应 ssl_certificate_key
  • chain.pem:仅中间证书,可用于 OCSP stapling 的 ssl_trusted_certificate

Nginx 里对应配置形如:

server {
    listen 443 ssl;
    server_name example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}

不要把私钥提交进 Git 仓库,也不要在容器镜像里内置,应通过密钥管理注入,参考环境变量与配置管理

六、监控与排障

自动续期偶尔会失败(如 DNS 解析变更、网络问题)。建议:

  • certbot renew --dry-run 定期演练;
  • 配置监控,在证书临近过期(如剩余 14 天)时告警,可接入监控告警
  • 留意 Let's Encrypt 发来的过期提醒邮件;
  • 了解速率限制:同一主域每周最多签发 5 张证书,频繁失败重试容易撞上限,这也是"让 renew 自动跑、别手动狂刷"的另一个原因。

验证线上证书是否已更新:可以检查证书的到期时间是否顺延:

echo | openssl s_client -servername example.com \
  -connect example.com:443 2>/dev/null | openssl x509 -noout -enddate

输出里的 notAfter 若是 90 天后的日期,说明续期和部署都正常。

常见问题renew 提示 "Failed to connect" 时先看 80/443 端口是否被占用(standalone 冲突);DNS-01 失败先检查 API 令牌权限与 TXT 记录传播延迟;证书续期后页面仍是旧证书,多半是 deploy-hook 没有执行成功。

关于证书类型与续期协议的新进展,可参考SSL 证书类型指南ACME ARI 自动续期

16IDC 观察

Let's Encrypt 的"免费 + 90 天 + 自动续期"模式,把证书管理从"一年手工折腾一次"变成了"配置一次、持续自动"。对独立站和中小团队,这意味着 HTTPS 的成本与心智负担都降到很低,而 hooks 机制让"签发-部署-生效"全链路可以真正无人值守。它之所以能这么省心,前提恰恰是那套"短周期 + 自动化"的设计:证书越短命,越逼着你把流程跑通、把监控挂上。只要把 --dry-run 演练和到期监控纳入日常运维,这套流程就足够可靠。

参考:https://eff-certbot.readthedocs.io/en/stable/using.html 、https://letsencrypt.org/docs/rate-limits/