证书链是什么,以及谁负责发送

一张网站证书不是单独成立的,它靠一条信任链往上追溯:你的证书(leaf)→ 中间证书(intermediate)→ 根证书(root)。验证方必须能一路连到自己信任的根证书,才会认可这张证书。

关键在于各段的来源不同。根证书预先内置在操作系统或浏览器的信任库里,不需要传送;但中间证书不在里面,必须由你的服务器连同 leaf 一起发送。服务器只发 leaf 的话,验证方手上就缺了中间那一段,链接不起来。

客户端 缺中间证书时的行为 用户看到
桌面浏览器 多半会依 AIA 字段自行下载,或沿用先前缓存的中间证书 一切正常,完全无感
移动 App 与 WebView 通常不会自行补齐 连接失败或证书警告
curl、wget 不会 证书验证失败,直接中断
程序的 HTTP 客户端(Python、Node.js、Java) 不会 抛出证书验证异常
监控与健康检查工具 不会 持续告警,或误判服务中断
服务器漏发中间证书时,各类客户端的反应。差异就是症状时好时坏的原因。

那为什么有些设备还是能正常连接?因为部分客户端会自己想办法补。下表是差异所在。

三种造成链不完整的情况

几乎所有案例都来自这三种操作,而且都不会在当下报错:

  1. 上传了只含 leaf 的证书文件 这是最常见的一种。证书机构通常会给你数个文件,其中一个只有你的证书本身,另一个才是包含中间证书的完整链。以 Let's Encrypt 为例,cert.pem 只有 leaf,fullchain.pem 才是完整的——配置时挑错文件,服务会正常启动,错误要等到特定客户端连进来才浮现。
  2. 手动安装时漏掉中间证书 在需要分别粘贴证书与中间证书的管理界面(许多主机商与负载均衡器都是这种设计),中间证书那一栏留空仍然可以保存成功。界面不会提醒你少了什么,因为从它的角度看,字段本来就是选填的。
  3. 证书机构更换了中间证书 证书机构会轮替自己的中间证书。续签后拿到的 leaf 可能由新的中间证书签发,但服务器上还放着旧的那一张——链一样接不起来。这种特别难查,因为「上次好好的、这次就坏了」,而你可能什么都没改。

为什么症状会时好时坏

桌面浏览器有两套补救机制。第一是证书里的 AIA 字段(Authority Information Access),它记载了签发者证书的下载网址;浏览器发现链断了,就照这个网址把中间证书抓下来自行补上。第二是缓存——浏览器会保留先前在其他网站看过的中间证书,下次遇到就直接沿用。

这两套机制加起来,让问题在浏览器上几乎隐形。更麻烦的是行为不一致:各家浏览器、各个版本对 AIA 抓取的支持程度不同,而缓存的内容取决于用户先前逛过什么网站。所以同样一台电脑、换个浏览器结果就不一样,甚至同一个浏览器在不同人手上表现也不同。

反过来说,非浏览器的客户端多半没有这些机制。它们拿到什么就验什么,缺了中间证书就直接失败。所以最先反馈问题的通常是 App、API 对接方,或是监控系统——而不是一般网页用户。

怎么确认服务器发送了什么

最直接的方式是用 openssl s_client -connect 域名:443 -servername 域名 -showcerts 连上去,它会列出服务器实际发送的所有证书。输出里每一个 s:(subject)与 i:(issuer)配对就是链的一段——如果只看到一张证书,那就是只发了 leaf。

正常的情况下,你会看到 leaf 的 issuer 等于下一张证书的 subject,一路串接到根证书或某张已知的中间证书为止。检查要从外部发起,而且要对着用户实际连到的地址——如果服务前面有 CDN,要确认你测的是哪一层,因为 CDN 节点与源站可能装着不同的证书。

三个层次的处理

确认问题存在之后,依序处理:

一、换成完整链的证书文件

把配置指向包含中间证书的那个文件(Let's Encrypt 是 fullchain.pem),或在管理界面把中间证书补进对应字段。根证书发不发都可以,多发一张只是多几百字节,不会造成错误。改完记得重新加载服务——文件换了但服务没 reload,等于没改,Nginx 的证书加载机制一文有详细说明。

二、确认每一个终止 TLS 的位置都改到

源站、每个 CDN 节点、负载均衡器都各自持有证书。只改了源站而 CDN 节点还是旧的,对用户而言问题依然存在。这一点跟证书到期的处理是同一个道理——证书问题几乎都出在「有些地方改了、有些没有」。

三、把检查纳入续签流程

证书机构轮替中间证书时不会通知你。真正能防住的做法,是在每次续签部署后自动确认链的完整性,而不是等用户反馈。这跟到期监控是同一套机制,只是多验一个项目。

排查时该先确认的三件事

在动手改配置之前,这三项能省下大量时间:

  1. 确认问题确实是链不完整 证书过期、域名不符、系统时间错误都会产生类似的错误信息。先用 openssl 看清楚服务器发送几张证书,再决定方向,避免改错地方。
  2. 确认你检查的是哪一层 有 CDN 的架构下,从外部测到的是边缘节点的证书。如果问题出在回源,那要另外测源站。搞错层级会得到完全相反的结论。
  3. 确认失败的客户端是哪一类 如果只有部分移动设备失败、桌面全部正常,中间证书缺失的可能性很高。如果连桌面浏览器都失败,那通常是别的问题——因为浏览器连链不完整都能自己补救。

小结

「我的浏览器打得开」在证书问题上不是有效的验证,因为浏览器会替你把缺失的中间证书补回来,而你的 App、API 客户端与监控系统不会。判断方式只有一个:检查服务器实际发送什么,而不是某个客户端最后有没有连上。

这类错误的根源通常是手动部署——挑错文件、漏填字段、或证书机构换了中间证书而没同步。如果不想每次都靠人工确认,我们的 SSL 证书自动化平台在签发后会把完整链直接部署到各节点,省掉手动粘贴证书这个最容易出错的环节。

参考资料