HTTPS证书自动续期发生故障,详细故障排查记录

测试环境说明

服务器:LuckVM 香港节点;环境:Nginx 1.24 + Certbot 2.7.4;证书:Let's Encrypt 免费 SSL 证书

给网站装了 HTTPS 证书之后,Let's Encrypt 的免费证书只有 90 天有效期,很多人配置完自动续期就不管了,结果 3 个月之后证书过期,网站打开提示不安全,才发现自动续期失败了。我上周就遇到了这个问题,crontab 里明明写了续期任务,结果到期没续上,查了半个多小时日志才找到原因,这篇把常见的自动续期失败原因和排查方法记下来,避免大家踩同样的坑。


一、先手动测试续期能不能成功

遇到自动续期失败,第一步永远是先手动跑续期命令,看报错信息是什么,90%的问题手动跑一次就能找到原因:

# 测试续期,不真的续期,dry run模式
certbot renew --dry-run

如果手动dry run成功,说明证书本身配置没问题,问题出在自动任务上;如果手动跑都报错,就根据报错信息一个个解决,常见的手动报错有这几个:

报错1:Timeout during connect (likely firewall problem)

原因:防火墙没放行 80 端口,或者 Nginx/Apache 没启动,Certbot 的 HTTP-01 验证需要访问服务器 80 端口下的验证文件,端口不通就验证失败。解决:

  1. 检查 ufw/安全组有没有放行 80 端口:ufw allow 80/tcp
  2. 确认 Nginx/Apache 正在运行:systemctl status nginx
  3. 如果开了 CDN(比如 Cloudflare),续期的时候暂时把 CDN 调成「开发者模式」,或者暂停 CDN,不然验证请求会到 CDN 节点上,到不了你的服务器

报错2:Invalid response from xxx/.well-known/acme-challenge/

原因:网站根目录配置错了,或者伪静态规则拦截了验证请求,Certbot 找不到验证文件。解决:

  1. 确认 Nginx 配置里的 root 路径是正确的网站根目录
  2. 如果有伪静态规则,在规则里加一行,把 .well-known 目录排除,不让伪静态拦截:

location ^~ /.well-known/acme-challenge/ {
root /var/www/你的网站根目录;
allow all;
}

  1. 手动在网站根目录创建 .well-known/acme-challenge/test.txt 文件,浏览器访问 http://你的域名/.well-known/acme-challenge/test.txt,确认能正常打开,能打开就说明路径没问题

报错3:No valid IP addresses found for 域名

原因:域名 DNS 解析错了,或者解析到了 CDN/其他 IP,certbot 解析域名找不到你的服务器 IP。解决:检查域名 DNS 解析是不是指向你的服务器公网 IP,有没有开 CDN 代理(橙云),续期的时候可以暂时把 CDN 关了,续完再开。


二、自动续期任务没运行的排查

如果手动续期成功,但是到期没自动续,那就是定时任务 crontab 的问题,一步步查:

  1. 先看 Certbot 有没有自动加 systemd 定时任务,现在新版 Certbot 安装完会自动加定时任务,不用自己写 crontab:

systemctl list-timers | grep certbot
# 能看到 certbot.timer 就说明系统自带定时任务,每天会自动检查续期,一般是到期前 30 天自动续
# 查看定时任务状态
systemctl status certbot.timer

如果系统自带了 certbot.timer,就不用自己再写 crontab 了,两个定时任务同时跑反而可能出问题。

  1. 如果是自己写的 crontab 任务,先检查 crontab 有没有写错:

crontab -l

正确的续期任务应该是这样的(每天凌晨3点检查续期,续完重载Nginx):

0 3 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"

⚠️ 常见crontab坑:

  • 没用certbot的绝对路径:crontab的环境变量和你手动登录的shell不一样,直接写certbot renew会找不到命令,必须用全路径/usr/bin/certbot,可以用which certbot看路径在哪
  • 时间写错了:比如写的是0 3 1 * *,那每个月1号才跑,要是证书到期不在1号就续不上,应该写每天都跑,certbot自己会判断需不需要续,没到期不会真的续
  • 没加重载Nginx的post-hook:就算证书续成功了,Nginx还是用的旧证书,必须重载Nginx才会加载新证书,等于白续
  • 跑任务的用户不对:crontab分用户,如果你是root用户写的任务,普通用户的crontab里不会有,要确认是root的crontab
  1. 看crontab日志,确认任务有没有运行:

grep CRON /var/log/syslog
# 看对应时间有没有certbot的运行日志,有没有报错


三、证书续了但网站还是显示旧证书

很多人续期成功了,但是浏览器打开还是提示证书过期,这是因为:

  1. 没重载Web服务:Nginx/Apache启动的时候会把证书加载到内存里,续完新证书存在磁盘上,必须重载Nginx才会重新读证书,所以续期命令一定要加--post-hook "systemctl reload nginx"
  2. CDN缓存了旧证书:如果开了Cloudflare或者其他CDN,CDN节点上缓存的还是旧证书,需要去CDN后台重新上传新证书,或者Purge CDN缓存
  3. 浏览器缓存了旧证书:按Ctrl+F5强制刷新页面,或者开无痕模式访问,排除本地缓存问题

四、我这次遇到的问题

这次我的证书续期失败原因特别蠢:之前开了 UFW 防火墙,默认拒绝入站,当时只放行了 443 和 SSH 端口,忘了放行 80 端口,Certbot 续期的时候用 HTTP-01 验证需要 80 端口,连不上就失败了。而且我写 crontab 的时候没加日志输出,任务跑失败了也没通知我,直到证书过期浏览器报警才发现。 解决方法:

  1. 放行80端口:ufw allow 80/tcp
  2. 在crontab里加日志,方便排错:

0 3 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx" >> /var/log/certbot-renew.log 2>&1

  1. 手动跑一次dry run确认成功,之后就正常了。

五、避坑总结

  1. 80 端口不要随便关,Let's Encrypt http-01 验证必须用 80 端口,就算你网站强制跳 HTTPS 也得开着 80
  2. 自动续期一定要加重载 Nginx/Apache 的钩子,不然续了也白续
  3. 定时任务要加日志输出,不然失败了根本不知道
  4. 不用自己写 crontab,新版 certbot 的 systemd timer 比自己写的 crontab 靠谱,默认安装就有,不会写错路径和参数
  5. 可以配置邮件通知,续期失败发邮件提醒,省得证书过期了才发现。

六、排查流程一图看懂

把上面的排查思路整理成一张流程图,遇到证书没续上的时候照着走一遍,基本能定位到问题:

flowchart TD A["发现证书过期 / 网站提示不安全"] --> B["手动执行 certbot renew --dry-run"] B --> C{"dry run 是否成功"} C -->|"失败"| D["看报错信息"] D --> D1["Timeout during connect<br/>检查 80 端口 / Web 服务 / CDN"] D --> D2["Invalid response<br/>检查 root 路径 / 伪静态规则"] D --> D3["No valid IP addresses<br/>检查 DNS 解析 / CDN 代理"] D1 --> E["修复后重新 dry run"] D2 --> E D3 --> E C -->|"成功"| F["问题在自动任务"] F --> G{"是否存在 certbot.timer"} G -->|"存在"| H["检查 timer 状态与日志"] G -->|"不存在"| I["检查 crontab 配置"] I --> I1["绝对路径 /usr/bin/certbot"] I --> I2["时间表达式是否每天执行"] I --> I3["是否加 --post-hook 重载 Nginx"] I --> I4["是否为 root 用户的 crontab"] H --> J["查看 CRON 日志确认是否执行"] I1 --> J I2 --> J I3 --> J I4 --> J E --> K["续期成功"] J --> K K --> L{"浏览器仍显示旧证书"} L -->|"是"| M["重载 Web 服务 / 清理 CDN 与浏览器缓存"] L -->|"否"| N["完成"] M --> N

七、常见问题与原因对照表

现象 最可能的原因 快速验证方式
Timeout during connect 80 端口未放行、Web 服务未启动、CDN 拦截 ufw status、systemctl status nginx
Invalid response from .../.well-known/ 网站根目录配置错误、伪静态规则拦截 浏览器访问 /.well-known/acme-challenge/test.txt
No valid IP addresses found DNS 解析错误、解析到 CDN 节点 dig 你的域名 对比服务器公网 IP
手动续期成功但到期未续 定时任务未配置或配置错误 systemctl list-timers | grep certbot、crontab -l
续期成功但浏览器仍报过期 未重载 Web 服务、CDN 或浏览器缓存 systemctl reload nginx 后强制刷新
posted @ 2026-11-03 15:25  前端已挂  阅读(2)  评论(0)    收藏  举报