发布时间:2026-09-13 04:38 更新时间:2026-09-13 04:38 阅读量:1
给站点配好 HTTPS 之后,很多人就把证书这件事抛在脑后了,直到某天浏览器弹出证书过期的警告,才发现 certbot 的自动续期早就悄悄失败了。自动续期是定时任务在后台跑的,失败时未必会发邮件提醒你,等发现时往往已经影响到正常访问。这篇文章把排查过程拆成三段:先看 renew 日志定位报错,再查 80 端口这条 HTTP-01 验证通道,最后把续期后的 nginx 重载钩子配好,让新证书真正生效。文中涉及 /etc/letsencrypt 目录的命令一般需要 root 权限,请按需加 sudo,具体行为以你所装版本和官方文档为准。
排查的第一步是搞清楚现在证书还剩多少天、续期任务到底有没有跑。执行下面的命令可以列出本机由 certbot 管理过的全部证书及其到期时间:
sudo certbot certificates输出里会标明证书名称(cert-name)、覆盖的域名、到期日期以及证书文件路径。如果剩余天数已经很少而没有被续期,说明要么定时任务没触发,要么触发了但验证失败。真正的报错信息在 certbot 的日志里,默认路径是 /var/log/letsencrypt/letsencrypt.log,日志会按大小滚动,所以先确认最近一次运行的时间:
sudo ls -lt /var/log/letsencrypt/ | head
sudo grep -iE "error|fail|timeout|refused" /var/log/letsencrypt/letsencrypt.log | tail -30日志里常见的表述集中在几类:连接目标超时、连接被拒绝、返回内容不符合预期、找不到对应的 server 块等。看到具体 URL 和状态码之后,问题基本就锁定在验证环节了。接着确认定时任务是否真的存在且处于启用状态,不同安装方式(系统包、snap、pip)注册的调度方式不一样,有的用 systemd timer,有的用 cron,两个都看一眼:
systemctl list-timers | grep -i certbot
systemctl status certbot.timer
sudo crontab -l | grep -i certbot在正式动手修之前,建议先用演练模式验证一遍。dry-run 走的是测试环境,不会消耗正式签发配额,可以放心反复跑:
sudo certbot renew --dry-run用 webroot 或 standalone 方式申请时,走的是 HTTP-01 校验:CA 会主动访问 http://你的域名/.well-known/acme-challenge/随机串,读到约定的内容才算通过。所以这个路径必须满足三个条件——80 端口对外开放、能返回 200、返回的是纯文本内容而不是跳转或错误页。失败的原因通常出在下面几处:域名前置了 CDN 或 WAF,没有回源或者拦截了这个目录;服务器前面还有另一层 Web 服务(比如容器里的 nginx)抢占了 80 端口;防火墙只放行了 443;以及整站强制跳转到 HTTPS,把校验请求也一起跳走,而现网证书又恰好过期,形成死循环。
比较稳妥的做法是在 80 端口的 server 块里把校验目录单独放行,再统一跳转。nginx 配置可以这样写:
server {
listen 80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type "text/plain";
try_files $uri =404;
}
location / {
return 301 https://$host$request_uri;
}
}这里用 ^~ 前缀匹配,是为了让这个 location 优先于后面可能出现的正则规则,避免校验文件被别的规则拦下。目录本身要提前建好,并保证 nginx 工作进程有读取权限(下面的用户按你的实际运行用户调整):
sudo mkdir -p /var/www/letsencrypt/.well-known/acme-challenge
sudo chown -R www-data:www-data /var/www/letsencrypt
sudo certbot certonly --webroot -w /var/www/letsencrypt -d example.com -d www.example.com自己验证通道是否通畅,可以放一个测试文件再用 curl 从本机或外网访问,重点看返回码和是否被重定向。也可以给 renew 加上 --debug-challenges 参数,certbot 会在发起校验前暂停并打印出校验文件的落盘路径,这时用浏览器或 curl 访问一次,能很快判断是路径不对还是网络不通。
证书文件更新了,不等于 nginx 立刻会用上,它需要重新加载配置或发信号才会读取新证书。certbot 支持在续期动作完成后执行钩子脚本,推荐统一放在 renewal-hooks 目录下,这样任何一张证书续期成功都会触发,不用每张证书单独配:
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh > /dev/null <<'EOF'
#!/bin/bash
set -e
nginx -t && systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh这段脚本的关键是先 nginx -t 检查配置语法,确认无误再 reload。直接重载一份有语法错误的配置,可能让服务无法正常工作;用 reload 而不是 restart,则能保持连接不断开,对线上业务更友好。如果你习惯在命令行里临时指定,也可以使用 certbot renew --deploy-hook "nginx -t && systemctl reload nginx",较新版本会把钩子记录进 /etc/letsencrypt/renewal/ 下对应的配置文件中,是否落盘以你所装版本的实际行为为准。
另外可以顺手检查续期配置文件,里面记录了验证方式、webroot 路径等信息:
sudo grep -E "webroot_path|_hook|authenticator|installer" /etc/letsencrypt/renewal/example.com.conf配好之后,用 dry-run 完整跑一遍演练,确认校验通过、钩子执行、nginx 正常重载,这条链路就算真正打通了。日常运维里,把到期时间纳入监控、偶尔手动跑一次续期演练,比等到浏览器报错后再救火要从容得多。证书有效期本身在逐步缩短,续期频率会越来越高,让自动化流程可靠,才是 HTTPS 长期稳定运行的基础。
| 📑 | 📅 |
|---|---|
| Docker 磁盘占用过高清理实战:overlay2、容器日志与悬空镜像 | 2026-09-12 |
| Nginx 502/504 排查实战:从 upstream 超时到 php-fpm 进程池 | 2026-09-12 |
| MySQL 出现 Too many connections 怎么办:定位、连接池与超时调优 | 2026-09-11 |
| Linux用户与权限管理实战:sudoers、SUID/SGID与最小权限 | 2026-09-10 |
| systemd服务管理实战:从Unit编写到开机自启与自动重启 | 2026-09-10 |
| Nginx 日志按天切割与过期清理:logrotate 实战 | 2026-09-15 |
| Docker Compose 部署多容器应用实战:环境变量、数据卷与重启策略配置要点 | 2026-09-15 |
| MySQL慢查询日志开启与优化入门 | 2026-09-15 |
| 服务器定时任务实战:crontab 语法与不生效排查 | 2026-09-15 |
| Nginx 499 状态码排查实战:客户端断连与 upstream 超时的区别 | 2026-09-16 |