续期成功了,Nginx 却还在用旧证书:证书热加载这个坑

ACME 自动续期跑得好好的,线上却在半夜报证书过期。多数时候问题不在签发,而在 Web 服务器没有 reload,新证书躺在磁盘上没人读。本文梳理这个断点的成因、排查方式和可靠的处置办法。

本文目录
续期成功了,Nginx 却还在用旧证书:证书热加载这个坑

半夜收到证书过期告警,登上机器一看,openssl x509 -in /path/to/cert.pem -noout -dates 显示证书两周前就续过了,有效期到三个月后。再用 openssl s_client -connect 打一下线上 443 端口,返回的却是旧证书,明天到期。

这个场景几乎每个运维都撞见过。签发链路没问题,问题出在最后一棒:证书文件换了,进程没换。

为什么 Nginx 不会自己捡新证书

Nginx 在启动或 reload 时把证书和私钥读进内存,之后不再盯着磁盘文件。这是合理设计——运行时频繁读文件既慢又引入失败面。代价是:磁盘上的 fullchain.pem 更新得再勤快,正在跑的 worker 进程依然拿着旧证书对客户端握手。

Apache、HAProxy、各类自研网关同理。所以"续期成功"和"生效成功"是两件事,中间隔着一次 reload。

Certbot 的 --deploy-hook 就是为这个断点设计的:只在证书真正更新后执行一次钩子,通常是 nginx -s reload 或 systemctl reload nginx。注意别用 --renew-hook 当替代品的心理安慰——新版 Certbot 里它和 deploy-hook 行为接近,但语义上 deploy-hook 才是"部署时执行",写文档和交接时少一层歧义。

排查时先分清是哪一环断了

续期成功了,Nginx 却还在用旧证书:证书热加载这个坑:DNS ownership verification using a delegated CNAME, shown as a precise handoff between two controlled zones
续期成功了,Nginx 却还在用旧证书:证书热加载这个坑:DNS ownership verification using a delegated CNAME, shown as a precise handoff between two controlled zones查看原图

遇到"证书没更新"告警,按顺序确认三件事:

  1. 磁盘上的证书是不是新的。看文件修改时间和 notAfter。
  2. 线上握手返回的是不是新证书。用 s_client 或浏览器看实际下发的证书链。
  3. 如果 1 新 2 旧,就是 reload 没发生或失败了。

第 3 步还有一种隐蔽变体:reload 执行了但失败了。比如私钥权限被改、配置里引用的路径指向旧文件(有人续期到 /etc/letsencrypt/live/ 下,Nginx 却读一份手工拷贝到别处的证书),reload 报错被 cron 的输出吞掉。钩子脚本里加一句 nginx -t && systemctl reload nginx,失败时至少能在日志里留个尸体。

容器环境更麻烦一点。容器内跑 Nginx 的话,宿主机的 reload 够不着它;Ingress 场景下证书往往挂在 Secret 上,更新后要确认控制器真的重新加载了。别假设"文件挂载是实时的"就等于"进程感知是实时的"。

监控要盯握手结果,别只盯文件

很多监控脚本检查的是本地证书文件的到期时间。这对单机直连够用,但文件新、握手旧的情况它完全看不见,CDN 或 LB 终止 TLS 的架构下更是直接瞎掉。

更可靠的做法是从外部探测实际握手返回的证书剩余天数,告警阈值设在自动续期触发时间之后——比如 Let's Encrypt 证书通常 30 天左右就会续,剩余 25 天还没变新就该响了,别等到剩 3 天。阈值贴着续期节奏定,告警才不会变成狼来了。

续期成功了,Nginx 却还在用旧证书:证书热加载这个坑:an ACME request being validated at separate stations and turned into a new deployable artifact while private key material remains at its origin
续期成功了,Nginx 却还在用旧证书:证书热加载这个坑:an ACME request being validated at separate stations and turned into a new deployable artifact while private key material remains at its origin查看原图

在 CertbotX 中实践

这类断点本质上是"签发"和"部署"两条流水线没接上。在 CertbotX 里,ACME 自动签发和到期前重新签发之后,可以直接配置向 Agent、EdgeOne 或 CDN 的自动部署,证书更新后由部署链路推送到目标,而不是依赖某台机器上某个 cron 钩子里的 reload 命令恰好没写错。每次部署留有记录,排查"到底哪一步没生效"时可以先翻审计记录,确认证书是否真正下发到了目标节点,再回头查进程侧的问题——比一台台机器翻 cron 日志省不少事。对 DNS-01 场景,CNAME 委托也避免了把主域 DNS 凭据散在各台续期机器上。

顺手检查一下私钥

既然聊到生命周期,补一句私钥:续期时默认很多客户端会复用私钥。复用本身不是错,但如果旧私钥有泄露嫌疑,"自动续期成功"反而会把风险延长——新证书、旧钥匙。需要轮换时确认客户端的 key 轮换选项是否开启,别在应急响应时才发现换了半天证书换的是皮。

---

参考核验:

← 返回资讯中心