证书没过期,为什么浏览器还是报警?聊聊证书生命周期里最容易翻车的三个环节
证书过期只是最容易被发现的一种问题。签发后旧证书残留、链不完整、续期后服务没重载,这三类故障在运维现场更常见。文章分析成因、排查顺序和处置方式,并说明自动化签发部署如何避免人为遗漏。
本文目录

凌晨两点的告警短信写着“证书即将到期”,你登上去一查,证书明明上周刚续过,有效期到明年。这种“过期但没过期”的戏码,几乎每个运维都遇到过。证书生命周期里的坑,大多不在签发那一刻,而在签发之后。
坑一:新证书签好了,旧证书还在跑
最常见的场景:续期脚本跑成功了,证书文件也更新了,但服务没重载。Nginx、HAProxy 这类进程把证书读进内存后,磁盘上的文件换了它并不知道。浏览器拿到的还是内存里那份旧证书。
排查方法很直接,绕过本地文件,直接看线上实际出示的证书:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject如果这里显示的到期时间和磁盘上证书文件对不上,就是重载没做。处置是 nginx -s reload 或对应服务的 reload 命令,然后再用上面的命令验一次——reload 命令返回 0 不代表新证书真的生效了。
注意 reload 和 restart 的区别:reload 是平滑的,老 worker 处理完存量连接再退出,新 worker 加载新配置。线上别图省事直接 restart,那一瞬间的瞬断在大流量下很可观。
坑二:证书链不完整,桌面能过、手机报错
这是个很折磨人的问题。服务器只配置了叶子证书,没带中间证书。桌面浏览器大多会自己去补齐中间证书(通过 AIA fetching),所以你本机访问一切正常;但很多 Android 老版本、Java 客户端、curl 不带这个能力,直接报 chain validation 失败。
表现就是“有的设备正常,有的设备报错”,而你在自己电脑上怎么也复现不出来。检查方法:用上面那条 openssl 命令看输出里的链,或者用 SSL Labs 一类的在线检测看 chain issues。处置是在 Nginx 的 ssl_certificate 里放完整链文件(fullchain),而不是单独的叶子证书。ACME 客户端签下来的一般都有 fullchain 文件,问题是有人手工配置时挑错了那个只有一张证书的文件。

坑三:多入口、多节点,漏了一处
证书续期在 A 机器上自动化了,但别忘了证书可能还挂在你忘了的地方:负载均衡器上另外配的一份、CDN 回源端的证书、测试环境用了同一个域名、甚至是运维同学半年前手工上传到某台边缘节点的副本。这些不在自动化覆盖范围内的副本,到期时会精准地在周末报警。
处置思路是建立一份“证书部署点清单”,哪怕只是写在 wiki 上的几行:这个域名的证书部署在哪些节点、哪些平台、由什么流程负责更新。续期后逐个核验,或者干脆把每个部署点都纳入自动化。这就像给房间换灯泡——只换了客厅,卧室的灯还是会黑。
在 CertbotX 中实践
这类问题的共同根源是“签发”和“部署”之间的缝隙靠人肉去填。在 CertbotX 中,签发走 ACME 流程(支持 DNS-01,配合 CNAME 委托可以不动主域名的 DNS 权限就做验证),证书到期前自动重新签发;签发完成后直接推送到 Agent、EdgeOne 或 CDN,省去了手工拷贝文件、手工重载这一步。每次部署都有记录可审计——哪张证书、什么时间、推到了哪里,排查“线上到底跑的是哪份证书”时不用再靠猜和翻 shell 历史。
它解决不了所有问题,比如你得先知道证书都部署在哪;但它能把“签好了但忘了部署/忘了重载”这一类人为遗漏从流程里挤出去。
一点排查顺序的建议
遇到证书告警,按这个顺序走比较快:先用 openssl 看线上实际出示的证书(别先看本地文件),确认到期时间和链完整性;再对照签发记录确认新证书是否已生成;最后检查部署和重载环节。十之八九,问题出在最后一步。
延伸阅读,适合核验本文细节:
- RFC 8555:ACME 协议规范——自动化签发与续期的协议基础
- Nginx 官方 HTTPS 配置文档——证书链文件、reload 行为等配置细节
- Let's Encrypt 官方文档——证书链、有效期与续期机制的权威说明