Let’s Encrypt 证书 90 天到期,自动化续期失效是「定时炸弹」——平时看不出问题,某天浏览器突然全站报「不安全」。本文列六种常见的续期失败原因与对应诊断方法。
先说结论:先查「为什么失败」,再决定怎么续
不要直接手动跑 certbot renew --force-renewal。ACME 有「失败次数限制」(同一域名每周 5 次),盲目重试会把自己锁死一周。
# 1. 看续期日志(最关键)
docker logs <certbot容器> 2>&1 | tail -50
1. 或
tail -100 /var/log/letsencrypt/letsencrypt.log
1. 2. 先做 dry-run 测试(不消耗次数)
certbot renew --dry-run
1. 3. 看证书实际剩余时间
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -dates原因一:验证方式失效(HTTP-01 的 80 端口问题)
ACME 要访问 http://yourdomain/.well-known/acme-challenge/xxx 验证域名控制权。以下都会失败:
- 80 端口被防火墙封了(或安全组没开)
- 80 全部 301 跳到 443,而跳转规则没放行
/.well-known/ - Nginx 配置里给了 443 强制跳转但 ACME challenge location 写错
- CDN 层拦了 80 请求(如 Cloudflare 的「始终 HTTPS」)
# 自测:模拟 ACME 的访问路径
curl -I http://example.com/.well-known/acme-challenge/test
1. 期望 404(路径不存在但可达),若 301/403 就是被跳转或拦了
1. 看 nginx 的 challenge 配置
grep -A5 "well-known" /etc/nginx/conf.d/*.conf正确配置(位置要在跳转规则之前):
server {
listen 80;
server_name example.com;
1. 先匹配 ACME,再跳转——顺序错了就永远走不到这里
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
default_type "text/plain";
try_files $uri =404;
}
location / {
return 301 https://$host$request_uri;
}
}原因二:DNS 记录变了
- 域名 A 记录指向了新 IP,但新服务器还没配好
- 多级子域名的 DNS 解析没跟上(如
blog.x-rush.cn的 CNAME) - DNS 传播未完成(TTL 还很长)
# 确认解析是否指向当前服务器
dig +short example.com A
curl -s ifconfig.me # 或 curl -s ipinfo.io/ip
1. 对比:解析出的 IP 是否等于本机公网 IP原因三:证书文件权限或路径错
certbot 写入证书时,如果你的容器以非 root 用户挂载了证书目录,可能写不进去。
# 看证书目录权限
ls -l /etc/letsencrypt/live/example.com/
ls -l /etc/letsencrypt/archive/
1. 看 certbot 容器的挂载与用户
docker inspect <certbot容器> --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
docker inspect <certbot容器> --format '{{.Config.User}}'
1. 常见问题:archive 目录属主 root 700,容器内非 root 写不进原因四:ACME 客户端版本过旧 / 协议不兼容
Let’s Encrypt 已停止支持老版 ACME 客户端(如早年的 acme-tiny、旧版 certbot 1.x)。同时某些第三方 ACME 服务已下线。
# 看版本
certbot --version
1. 建议 2.x 以上
1. 若用 acme.sh
acme.sh --version
1. 注意:部分 ACME 端点已失效(如某些商业 CA 的免费接口),
1. 报 404 或 "unauthorized" 时要换回 Let's Encrypt 官方端点我们实测遇到过:
acme.sh配了某商业 CA 的免签接口,对方端点下线后一直报 404,日志里看不出是「端点失效」还是「配置错」。换回 Let’s Encrypt 官方端点后立刻正常。
原因五:续期任务的 cron 时间不对
- cron 任务被注释掉了(升级脚本时误删)
- cron 里用的路径是旧路径(迁移目录后没改)
- 容器重启后 cron 没跑(如果用
@reboot触发但容器还没起来) - 续期成功后 Nginx 没 reload:证书更新了但 Nginx 还加载着旧证书
# 看 cron
crontab -l | grep -iE "certbot|acme|renew"
1. 看确切的续期脚本
cat /path/to/renew.sh
1. 关键:续期成功后必须 reload nginx
1. renew.sh 里应有:
docker exec <nginx容器> nginx -s reload
1. 或
docker exec <nginx容器> nginx -t && docker exec <nginx容器> nginx -s reload原因六:域名数量太多或含已弃用域名
一张证书包含多个 SAN(如 x-rush.cn + blog + www),任何一个域名验证失败,整张证书都续不了。
# 看证书包含哪些域名
openssl x509 -in fullchain.pem -noout -ext subjectAltName
1. 逐个测试可达性
for d in example.com www.example.com blog.example.com; do
echo -n "$d: "
curl -sI "http://$d/.well-known/acme-challenge/test" | head -1
done处理:把已不用的域名从证书里移除(用 --cert-name 重新签发一张不含它的证书)。
一条完整的诊断流程
# 1. 剩余天数
EXP=$(openssl x509 -in /path/fullchain.pem -noout -enddate | cut -d= -f2)
echo "到期: $EXP"
1. 2. 最近一次续期结果
docker logs <certbot容器> 2>&1 | grep -iE "error|fail|success" | tail -10
1. 3. dry-run(不消耗次数)
certbot renew --dry-run 2>&1 | tail -20
1. 4. 若 dry-run 失败,看具体哪个域名/哪个验证方式
1. 日志里会有 "Failed authorization procedure"
1. 5. 验证路径可达性
curl -I http://example.com/.well-known/acme-challenge/test
1. 6. 检查 nginx 配置顺序(challenge 必须在跳转之前)
grep -B2 -A8 "acme-challenge" /etc/nginx/conf.d/*.conf总结
- 先 dry-run 再动手:ACME 有失败次数限制(每周 5 次),盲目 force-renew 会锁死一周
- 80 端口的 challenge 路径必须在 301 跳转之前:这是最常见也最容易忽略的配置错误
- 多域名证书是「全或无」:任一个域名验证失败,整张证书都续不了
- 续期成功后必须 reload nginx:否则证书文件更新了,进程里还是旧的
- <优先查日志里的 “Failed authorization procedure”:它会直接告诉你是哪个域名、用哪种验证方式失败