Let's Encrypt证书自动续期失败排查:certbot renew定时任务与webroot验证

    发布时间:2026-09-15 12:32 更新时间:2026-09-15 12:32 阅读量:0

    很多站长在服务器上装好 Let's Encrypt 证书后就不再管它,直到某天浏览器弹出证书过期警告,网站流量和信任度一起受影响。Let's Encrypt 签发的证书有效期是 90 天,官方推荐在到期前 30 天内自动续期,certbot 安装时会顺手写入一个 systemd 定时器或 cron 任务。问题在于,这套自动机制一旦失败,通常只有日志里留痕,没有邮件也没有面板提醒,等你发现时往往已经过期。下面按“定时任务有没有跑 → 验证方式能不能过 → 80 端口有没有被占”的顺序,把常见原因和检查命令梳理一遍,方便逐项对照排查。

    第一站:确认定时任务真的在跑

    先弄清楚你的 certbot 是用 systemd timer 还是 crontab 调度的。用 apt 或 yum 安装的包通常自带 systemd 单元,源码安装或老环境可能是 cron。两条命令分别看一眼:

    systemctl list-timers | grep -i certbot
    systemctl status certbot.timer
    crontab -l | grep -i certbot

    如果 timer 存在但显示 inactive (dead),说明它没有启用,执行 systemctl enable --now certbot.timer 即可。如果用的是 cron,常见坑是任务里写了相对路径导致找不到 certbot,建议在脚本里用绝对路径,例如 /usr/bin/certbot renew,并把输出重定向到日志文件,方便事后翻查:

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

    另外注意,certbot 的 renew 只会对“距到期 30 天以内”的证书发起续期,所以手动执行时看到 not due for renewal 是正常现象,不代表坏了。想强制验证流程是否通畅,可以加 --dry-run 做一次模拟,它不会消耗 Let's Encrypt 的速率限制:

    certbot renew --dry-run

    这条命令的输出基本就告诉你问题出在哪一环了:DNS 解析、HTTP 验证、还是 Nginx 配置。以官方文档和实际环境为准,不同版本输出文字略有差异。

    第二站:webroot 验证路径与 Nginx 配置

    webroot 验证的原理是:certbot 把一个临时校验文件写到网站根目录下的 .well-known/acme-challenge/,然后 Let's Encrypt 从公网访问 http://你的域名/.well-known/acme-challenge/文件名 来核对内容。这个环节失败的典型原因有三类:路径写错、Nginx 没有把该目录放行、以及 HTTPS 强制跳转把验证请求也 301 走了。

    先确认续期配置文件里记录的 webroot 路径与实际站点根目录是否一致。查看方式:

    certbot certificates
    ls -la /etc/letsencrypt/renewal/

    打开对应的 域名.conf,检查 webroot_path 这一项。很多人搬家或改过站点目录后忘了同步这里,导致 certbot 往旧目录写文件,公网自然访问不到。Nginx 侧要确保放行该目录,且不要被伪静态规则吞掉。参考配置如下:

    server {
        listen 80;
        server_name example.com www.example.com;
    
        location ^~ /.well-known/acme-challenge/ {
            root /www/wwwroot/example.com;
            default_type text/plain;
            allow all;
        }
    
        location / {
            return 301 https://$host$request_uri;
        }
    }

    这里有两个细节容易踩:一是 location ^~ 的优先级要高于普通 location,避免被 location / 的跳转规则截胡;二是如果站点做了全站 HTTPS 强制跳转,务必让 /.well-known/ 目录走 80 端口明文返回,否则验证请求被 301 到 HTTPS,而证书又正好过期,就形成了死循环。改完配置别忘 nginx -t 测试再 reload。

    第三站:80 端口被占用与验证失败的其他原因

    用 standalone 模式续期的用户,最容易撞上端口冲突。standalone 会临时启动一个监听 80 端口的服务来应答验证,如果此时 Nginx、Apache 或其他进程已经占着 80,certbot 就会报 Problem binding to port 80 之类的错误。检查命令:

    ss -lntp | grep ':80 '
    lsof -i :80
    systemctl status nginx

    解决办法有两种:要么续期前先停掉占用 80 的服务(配合 pre/post hook 自动停启),要么干脆改用 webroot 或 Nginx 插件模式,让它复用现有 Web 服务,不用抢端口。用 hook 的写法示例:

    certbot renew --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx"

    除了端口,还有几类高频原因值得一并排查:域名 DNS 解析被改到别的服务器、CDN 回源异常导致验证请求打不到源站、防火墙或安全组没有放行 80 端口、以及 Let's Encrypt 的速率限制(同一域名一周内重复签发次数有上限,触发后需要等待)。这些情况在 /var/log/letsencrypt/letsencrypt.log 里都有明确记录,出错时优先看这个文件,比凭感觉猜快得多。

    最后给一个省心的做法:把续期结果主动通知出来。certbot 支持 --deploy-hook,只在真正续期成功时触发,可以在这里调用邮件、企业微信或 Uptime Kuma 的推送接口,做到“失败有人知道”。证书是网站的信任底座,自动续期不是装完就完事,定期用 certbot renew --dry-run 跑一遍,把隐患掐在到期之前,比过期后手忙脚乱补签要划算得多。

    继续阅读

    📑 📅
    Nginx反向代理缓存实战:proxy_cache_path配置与命中率排查 2026-09-15
    大文件上传总失败:php.ini与Nginx三处限制如何协调放行 2026-09-14
    phpMyAdmin导入大SQL超时:参数调整与命令行导入 2026-09-12
    HTTPS证书有效却提示不安全:混合内容的定位与批量修复 2026-09-12
    服务器磁盘被写满的排查流程:df与du定位、日志切割与清理注意事项 2026-09-11
    WordPress数据库膨胀排查:wp_options自动加载与孤立表清理 2026-09-15
    宝塔面板计划任务做网站自动备份:数据库与文件打包、异地存储配置 2026-09-15
    CDN回源配置怎么填:回源Host、回源协议与真实IP排查 2026-09-16
    WordPress固定链接改版后老链接404:rewrite与301重定向保住收录 2026-09-16
    宝塔面板MySQL与PHP版本切换:兼容性检查、扩展重装与白屏处理 2026-09-16