证书过期不是"网站变黄",是浏览器直接红屏拒绝访问,用户没法点"继续访问"跳过。而这件事完全可以不发生——Certbot 装上之后,剩下十年你都不用再管证书。
这篇按"能直接复制跑"的标准写:装、签、自动续、验证、告警、排错,外加 2027 年证书有效期缩短前你现在就该做的三件事。
一、结论先给:四步跑完,之后全自动
# 1)安装(Ubuntu / Debian,snap 版版本最新,推荐)
sudo apt remove -y certbot 2>/dev/null
sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/bin/certbot
# 2)签发(--nginx 插件会自动改 Nginx 配置并加 80→443 跳转)
sudo certbot --nginx -d example.com -d www.example.com
# 3)确认自动续期的定时器在跑
sudo systemctl status snap.certbot.renew.timer # 要看到 active (waiting)
# 4)干跑验证(不真签发,专门用来提前暴露问题)
sudo certbot renew --dry-run
第 4 步必须当天做。定时器在跑不等于能续成功——端口被墙、DNS 改了、Nginx 配置被改坏,都会在真要续期的那一天才炸。
二、装哪个版本:snap 还是 apt
| 安装方式 | 版本 | 自动续期载体 | 建议 |
|---|---|---|---|
snap install --classic certbot |
官方最新 | systemd timer snap.certbot.renew.timer |
推荐,Ubuntu 24.04 首选 |
apt install certbot python3-certbot-nginx |
发行版仓库,通常偏旧 | certbot.timer 或 /etc/cron.d/certbot |
内网离线环境可用 |
dnf install certbot python3-certbot-nginx(RHEL / Rocky / Alma) |
仓库版本 | certbot-renew.timer |
需要 epel-release |
先查版本,低于 4.1.0 的一定要升级:
certbot --version
4.1.0(2025-06 发布)开始,certbot renew 会自动读取 ARI(ACME Renewal Information)——由 CA 告诉客户端"这张证现在该续了",而不是傻等写死的天数。这在后面 2027 年那节是硬要求。
两种安装方式不要混装,否则 which -a certbot 会出现两个,定时任务跑的可能不是你以为的那个。
三、签发:三种验证方式怎么选
| 方式 | 命令 | 适用场景 | 注意点 |
|---|---|---|---|
--nginx 插件 |
certbot --nginx -d a.com -d www.a.com |
Nginx 在跑,想让它顺手改配置 | 改完自动 test + reload |
| webroot | certbot certonly --webroot -w /var/www/html -d a.com |
只想拿证书,配置自己写 | 要求 80 端口可访问 /.well-known/ |
| standalone | certbot certonly --standalone -d a.com |
机器上没有 Web 服务 | 需要临时占用 80,得先停 Nginx |
| DNS-01 | certbot certonly --dns-cloudflare ... |
通配符证书 *.a.com |
只有 DNS-01 能签通配符,要 DNS 服务商 API 密钥 |
通配符只能走 DNS-01,别浪费时间试 HTTP-01:
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflare
# /root/cloudflare.ini 里放 dns_cloudflare_api_token,权限 chmod 600
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /root/cloudflare.ini \
-d example.com -d '*.example.com'
用 webroot 时,Nginx 的这个 location 要常驻,否则续期那天 404:
location ^~ /.well-known/acme-challenge/ {
root /var/www/html;
default_type "text/plain";
}
location / { return 301 https://$host$request_uri; }
四、自动续期的原理(知道它什么时候真干活)
sudo systemctl cat snap.certbot.renew.timer
# OnCalendar=*-*-* 00,12:00:00 每天 0 点和 12 点各一次
# RandomizedDelaySec=... 随机延迟,避免全球机器同时打 CA
关键点:
- 一天两次只是"检查",不是"续期"。默认只在剩余有效期 ≤ 30 天时才真的发起续期(配置在
/etc/letsencrypt/renewal/域名.conf的renew_before_expiry)。 - Certbot 4.1.0+ 会采纳 ARI 建议的续期窗口,大致是有效期的三分之二处,比写死 30 天更合理。
- 续期成功会写日志到
/var/log/letsencrypt/letsencrypt.log,排查只看这一个文件就够。
想手动确认当前所有证书的状态:
sudo certbot certificates
# Expiry Date: 2026-12-21 (VALID: 89 days)
五、续完必须让 Nginx 吃到新证书
证书文件换了,但 Nginx 内存里还是旧的,不 reload 就继续用旧证,直到过期。三种做法选一个:
做法 A(推荐):放 deploy hook,全局生效
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh >/dev/null <<'EOF'
#!/bin/bash
systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
放在 renewal-hooks/deploy/ 下的脚本,只在真的续期成功时执行,比每次定时器跑都 reload 干净得多。
做法 B:命令上直接带
sudo certbot renew --quiet --deploy-hook "systemctl reload nginx"
做法 C:用 --nginx 插件签发的,插件自带 reload,不用管。
注意用 reload 不是 restart:reload 不断连接,restart 会短暂中断。
六、没有 systemd 就用 crontab
老系统或者容器里没有 systemd 时:
sudo crontab -e
# 每天 3:30 和 15:30 各检查一次,随机睡 0-30 分钟错峰
30 3,15 * * * sleep $((RANDOM \% 1800)) && /usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"
Cron 表达式别写错,写错就是"以为在跑其实没跑":Cron 表达式在线解析 可以直接验证。
检查有没有真的执行过:
grep -i certbot /var/log/syslog | tail -20
# 或 systemd 环境:
journalctl -u snap.certbot.renew.service --since "7 days ago"
七、监控告警:这一步 90% 的人没做
Let's Encrypt 从 2025-06-04 起已经不发"证书即将过期"邮件了,所有账户邮箱都删了。也就是说:续期失败 → 没人告诉你 → 到期当天红屏。
自己加一道检查,30 行以内:
sudo tee /usr/local/bin/cert-expiry-check.sh >/dev/null <<'EOF'
#!/bin/bash
# 剩余天数低于 20 天就告警(可接企业微信/钉钉/飞书 webhook)
WARN_DAYS=20
WEBHOOK="https://your-webhook-url"
for cert in /etc/letsencrypt/live/*/fullchain.pem; do
[ -f "$cert" ] || continue
name=$(basename $(dirname "$cert"))
end=$(openssl x509 -enddate -noout -in "$cert" | cut -d= -f2)
left=$(( ($(date -d "$end" +%s) - $(date +%s)) / 86400 ))
if [ "$left" -lt "$WARN_DAYS" ]; then
msg="[证书告警] $name 剩余 $left 天($end)"
echo "$msg"
curl -s -X POST "$WEBHOOK" -H 'Content-Type: application/json' \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$msg\"}}" >/dev/null
fi
done
EOF
sudo chmod +x /usr/local/bin/cert-expiry-check.sh
再挂到 crontab,每天上午跑一次:
0 9 * * * /usr/local/bin/cert-expiry-check.sh
外部视角也值得加一道:HTTP 状态码查询 和 HTTP/Ping 检测 从站外看证书链是否正常、有没有中间证书缺失——有些问题本机 openssl 看不出来,浏览器却会报错。
八、续期失败的常见原因(按出现频率排)
| 报错 | 真因 | 处理 |
|---|---|---|
Connection refused / Timeout during challenge |
80 端口不通(ufw、云安全组、CDN 回源) | sudo ufw allow 80/tcp + 检查云厂商安全组 |
DNS problem: NXDOMAIN |
域名 A 记录没指向这台机,或已解析到别处 | dig +short 域名 核对 |
Unauthorized / 404 挑战文件 |
/.well-known/acme-challenge/ 被 301 到 HTTPS 或被 location 拦 |
加第三节那段 location |
too many certificates already issued |
触发速率限制(同域名每周 50 张、重复证书每周 5 张) | 等一周;调试一律加 --dry-run 或 --staging |
nginx: [emerg] 之后 certbot 改不动配置 |
Nginx 配置本身有错 | 先 nginx -t 修好再续 |
| 证书明明续了但浏览器还报旧日期 | 没 reload,或 CDN 上还挂着旧证 | systemctl reload nginx + CDN 侧重新上传证书 |
调试请一律走 staging 环境,不消耗正式额度:
sudo certbot renew --dry-run # 干跑,推荐
sudo certbot certonly --staging ... # 走测试 CA
九、2027 年之前必须改的三件事
Let's Encrypt 已公布时间表:2027-02-10 起默认证书从 90 天降到 64 天,2028-02-16 进一步降到 45 天(想提前适应,现在可以用 tlsserver profile 拿 45 天证书)。
对自动续期的影响:
- Certbot 升到 4.1.0 以上,让 ARI 决定续期时机。写死"每 60 天续一次"的脚本,在 45 天时代必然有一次来不及。
- 检查频率提高:一天两次够了,但确认定时器真的 enable 了:
systemctl is-enabled snap.certbot.renew.timer。 - 告警必须有:有效期越短,一次失败留给你的补救窗口越小。20 天告警线可以,45 天证书建议提到 15 天。
一句话:把"什么时候续"交给 ARI,把"续没续上"交给监控,人只需要在告警时介入。
十、小结
| 动作 | 命令 |
|---|---|
| 装 | snap install --classic certbot |
| 签 | certbot --nginx -d 域名 |
| 续(自动) | systemd timer 每天两次,剩余 ≤30 天或按 ARI 触发 |
| 重载 | /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh |
| 验证 | certbot renew --dry-run、certbot certificates |
| 告警 | 剩余天数脚本 + 企业微信/钉钉 webhook |
配完之后建议每季度做一次 certbot renew --dry-run,成本 10 秒,能挡掉 99% 的"到期才发现"。
相关工具:Linux 命令速查 · SSH 密钥检查 · Cron 表达式在线解析 · HTTP 状态码查询
延伸阅读:Nginx 配置参数解析与优化 · Nginx 跑满并发:Nginx + 系统内核调优 · Ubuntu 安装 Docker