证书自动续期成功了,浏览器却还在报警:Nginx 没 reload 的老坑

ACME 自动续期后证书文件已更新,但 Nginx 工作进程仍持有旧证书,导致浏览器持续报证书过期。本文分析这一常见故障的根因、排查方法和可靠的处置方式,包括 reload 钩子和自动化部署的衔接。

本文目录
证书自动续期成功了,浏览器却还在报警:Nginx 没 reload 的老坑

一个典型现场

半夜告警:证书还有 3 小时过期。登上机器一看,Certbot 明明三天前就续期成功了,/etc/letsencrypt/live/ 下的证书有效期是新的一轮。但 openssl s_client -connect 打过去,返回的还是旧证书,到期时间纹丝不动。

这不是 ACME 出问题,也不是浏览器缓存。原因朴素得让人不好意思:Nginx 没有 reload,worker 进程内存里还拿着启动时加载的旧证书。

为什么证书换了,服务发的还是旧的

Nginx 处理 TLS 握手时用的是 master 进程加载进内存、再随 fork 传给 worker 的证书和私钥。磁盘上文件变了,它不会自己去重读。必须显式触发一次配置重载:

nginx -s reload
# 或
systemctl reload nginx

reload 的逻辑是 master 重新读配置、拉起新 worker、旧 worker 处理完手头连接后退出。这一步之后才轮到新证书上岗。证书签发工具和 Web 服务器之间的这一脚,是很多"自动化"方案里最容易断掉的一环——签发是自动的,生效靠运气。

证书自动续期成功了,浏览器却还在报警:Nginx 没 reload 的老坑:DNS ownership verification using a delegated CNAME, shown as a precise handoff between two controlled zones
证书自动续期成功了,浏览器却还在报警:Nginx 没 reload 的老坑:DNS ownership verification using a delegated CNAME, shown as a precise handoff between two controlled zones查看原图

还有几种相似的变体:

  • HAProxy、Envoy、某些自研网关同理,进程内持有证书,换了文件不热加载就白搭。
  • 多服务共用一套证书:Nginx reload 了,但旁边还有个 mail 服务或内网网关挂着同一份证书,漏了其中一个,告警照样响。
  • 多机负载均衡:签发只在一台机器上完成,其他节点没同步,用户命中哪台全看运气,故障表现为"时而报警时而不报",最难排查。

排查套路

遇到"证书已续期但仍报过期",按这个顺序查,基本几分钟能定位:

  1. 直接看服务端实际发什么证书,别信浏览器:

`` echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates ``

  1. 对比磁盘上证书的日期:
证书自动续期成功了,浏览器却还在报警:Nginx 没 reload 的老坑:an ACME request being validated at separate stations and turned into a new deployable artifact while private key material remains at its origin
证书自动续期成功了,浏览器却还在报警:Nginx 没 reload 的老坑:an ACME request being validated at separate stations and turned into a new deployable artifact while private key material remains at its origin查看原图

`` openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -dates ``

  1. 两者日期不一致 → 进程没 reload;日期一致但浏览器仍报错 → 问题在别处(比如 CDN 边缘节点还缓存着旧证书,或链不完整)。
  2. 有多台机器的,逐台打一遍第 1 条。LB 后面藏着一台没同步的旧节点是经典剧情。

顺手提醒一个相关但不相同的坑:如果只 reload 了证书文件本身、而签发工具换了目录路径(比如换了域名导致 live/ 下的目录名变了),reload 会因为找不到文件而失败,Nginx 继续用旧配置跑。nginx -t 先于 reload 是个好习惯。

让续期真正"闭环"

处置思路只有一个:把"签完证书"和"让服务用上证书"绑成一个原子动作,中间不留人工环节。

Certbot 自带 deploy hook,续期成功且证书确有更新时才执行:

certbot renew --deploy-hook "systemctl reload nginx"
证书自动续期成功了,浏览器却还在报警:Nginx 没 reload 的老坑:a new certificate version moving from its source to several edge appliances while the previous working version remains ready for rollback
证书自动续期成功了,浏览器却还在报警:Nginx 没 reload 的老坑:a new certificate version moving from its source to several edge appliances while the previous working version remains ready for rollback查看原图

注意它只在实际换了证书时触发,不会每次检查都 reload,这点设计得挺体贴。

在 CertbotX 中实践

手动维护 deploy hook 在多机、多入口的场景下很快会变成一堆散落各处的脚本。在 CertbotX 里,这条链路是连起来的:ACME 自动签发(含 DNS-01 的 CNAME 委托,方便把验证域名交给专门区域)、到期前自动重新签发,然后直接向 Agent、EdgeOne 或 CDN 推送部署,部署动作本身有记录可审计。也就是说"签发—部署—确认生效"在一个流程里完成,排查时可以从部署记录直接看到某台节点是否真正换上了新证书,而不是靠 SSH 逐台去猜。

最后两个边界情况

  • CDN / 边缘节点证书独立:源站证书换了,CDN 上配置的是另一份证书,用户看到的以 CDN 为准。排障时先确认流量链路里哪一环在终止 TLS。
  • HSTS 下的证书错误没有"继续访问"按钮:一旦证书出问题且站点开了 HSTS,用户被完全挡在门外,故障影响面会陡然放大。这也是证书自动化必须做扎实的一个理由,而不是不做 HSTS 的理由。

---

← 返回资讯中心