证书明明没过期,浏览器却报“证书不受信任”:聊聊证书链缺失

服务器只发了叶子证书、没带中间证书,是“证书不受信任”告警里最常见的原因之一。本文解释证书链的构成、为什么有的客户端能正常访问、以及如何排查和补齐中间证书。

本文目录
证书明明没过期,浏览器却报“证书不受信任”:聊聊证书链缺失

半夜收到告警:证书还有半年才过期,但部分用户浏览器报“您的连接不是私密连接”。先别急着怀疑 CA 出了问题——这类故障十有八九是证书链缺失(incomplete chain):服务器只发了终端实体证书(叶子证书),没把中间证书一起发出去。

证书链是怎么一回事

浏览器或操作系统默认只信任一小撮根证书。而你手里的站点证书几乎从来不是根直接签的,中间隔着一层甚至两层中间 CA。验证时,客户端要从叶子证书一路往上爬到本地信任的根,这条路就是证书链。

TLS 握手里,服务器应该发送的是“叶子 + 全部中间证书”。根证书不用发——客户端本地有,发了也白发。如果中间证书没发,客户端手里只有叶子,往上找不到路,验证就断了。

为什么“有的设备正常,有的报错”

这是最容易让人误判的地方。你换了三台手机测试都没事,客户那边却打不开,常见原因有两个:

  • 缓存差异。很多浏览器会缓存曾经见过的中间证书。用户之前访问过同一 CA 签的其他站点,中间证书已经在本地缓存里,于是链路“碰巧”能补上。新设备、隐私模式、冷门客户端就没这个运气。
  • AIA fetching。部分客户端(典型是 Windows 上的浏览器)支持通过证书里的 AIA(Authority Information Access)扩展去下载缺失的中间证书;但不少命令行工具、Java 应用、嵌入式设备不做这件事,拿不到就直接失败。
证书明明没过期,浏览器却报“证书不受信任”:聊聊证书链缺失:DNS ownership verification using a delegated CNAME, shown as a precise handoff between two controlled zones
证书明明没过期,浏览器却报“证书不受信任”:聊聊证书链缺失:DNS ownership verification using a delegated CNAME, shown as a precise handoff between two controlled zones查看原图

所以“我这边能开”从来不是排查的终点。用 openssl s_client -connect example.com:443 -servername example.com 看一下返回的证书数量:只返回 1 张基本就是链不全;返回 2~3 张(叶子 + 中间)才是正常配置。也可以把证书丢给在线链检查工具确认。

怎么修

签发机构给你的文件里,通常有单独的 fullchain 或 bundle 文件,那就是叶子加中间。常见错误是配置时只引用了叶子证书文件。比如 Nginx 的 ssl_certificate 指向的是 fullchain 而不是只有叶子的那个文件;拼接顺序也要注意,叶子在前,中间证书按从近到远的顺序跟在后面。改完重载,再用上面的命令验证返回张数。

还有一个坑:同一 CA 可能有多个中间证书(比如为了兼容老设备做的交叉签名),别拿错链。用错中间证书的报错和缺中间证书几乎一样。

在 CertbotX 中实践

用 ACME 自动签发时,这个问题可以少操一份心。CertbotX 在 DNS-01(含 CNAME 委托到专用解析区)完成验证后,会保存签发返回的完整链,自动部署到 Agent、EdgeOne 或 CDN 时使用的也是带中间证书的完整链,而不是只推叶子证书。每次到期前重新签发后重新部署,部署记录里可以回看推了哪份链、推到哪个目标,出现“部分客户端报错”时便于对照排查。中间证书偶尔会因 CA 侧轮换而变化,自动化的好处是新链随重签一起更新,不用人工记着换文件。

---

← 返回资讯中心