中间证书到底是什么:为什么浏览器没报错,手机却连不上
中间证书是根证书和站点证书之间的信任桥梁,服务器必须把它一并发送。漏发后桌面浏览器能自动补齐,移动端和 API 客户端却常常直接报错,本文讲清这个常见又容易误判的问题。
本文目录

一个典型的奇怪告警
运维群里很经典的一幕:HTTPS 上线后自己用 Chrome 测没问题,客户却反馈 App 打不开,cURL 一跑就是 unable to get local issuer certificate。证书明明没过期,域名也对,查了一圈最后发现是服务器少发了一张中间证书。
这个问题的核心在于:浏览器会"帮忙",而大多数程序不会。
中间证书是什么

CA 通常不直接用根证书签发你的站点证书,而是用一张(或几张)中间证书来签。于是信任链长这样:
- 根证书(预置在操作系统或浏览器的信任库里)
- 中间证书(由根签发,用来签站点证书)
- 站点证书(由中间证书签发,绑定了你的域名)
TLS 握手时,服务器需要把站点证书和中间证书一起发给客户端。客户端拿到整条链,才能从自己信任的根一路验证到站点证书。根证书本身不用发,客户端信任库里已经有了。
可以这样理解:根 CA 是总部,中间证书是盖了总部章的介绍信,你的站点证书是凭介绍信办出来的工牌。门卫只认总部,所以介绍信你得随身带着。
为什么浏览器没问题,别的客户端却报错

桌面浏览器普遍支持 AIA fetching:证书里有一个 Authority Information Access 扩展,写着中间证书的下载地址。服务器没发,浏览器就自己下载补齐,于是看起来一切正常。
但 cURL、Java、Go 的 HTTP 客户端、不少移动端网络库和硬件设备不做这件事。链断在中间,验证直接失败。所以"我这边明明能打开"不代表链路完整,这是这个坑最隐蔽的地方。
类似的陷阱还有:某些浏览器缓存了中间证书,你换了一台新机器、换了个网络,问题就突然出现了,让人怀疑人生。
怎么排查和修复

先确认服务器实际发了哪些证书:
openssl s_client -connect example.com:443 -servername example.com看输出里 Certificate chain 一节。如果只列了一张站点证书(编号 0 之后没有后续证书),就是漏链了。
修复方式取决于 Web 服务器,但思路一致:把站点证书和中间证书合并成一个 fullchain 文件再配置。以 Nginx 为例,ssl_certificate 指向的文件里应该依次包含站点证书和中间证书:
-----BEGIN CERTIFICATE-----
(你的站点证书)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(中间证书)
-----END CERTIFICATE-----
顺序不能反:站点证书在前,中间证书在后。如果只放了站点证书,Nginx 不会报错,但握手时链就是不完整的。Let's Encrypt 签发的 fullchain.pem 本身就是按这个顺序拼好的文件。
改完记得重载配置,并用上面的 openssl 命令再验证一次,或者换一个没缓存过中间证书的环境实测。
在 CertbotX 中实践
用 ACME 自动签发证书时,CertbotX 交付的本身就是完整的证书链文件,部署到 Agent、EdgeOne 或 CDN 时也会连同中间证书一起下发,避免手工拼接时漏文件、顺序颠倒这类问题。每次到期前自动重新签发后重新部署,部署记录里有据可查,出现链不完整告警时可以回溯当时实际部署的内容。

什么时候会再遇到它
- 换 CA 或换证书品牌后,中间证书变了,但服务器还引用着旧的 chain 文件。
- 手动申请证书时,CA 给了好几个文件(cert、intermediate、root),漏拼了一个。
- 内部 PKI 自建 CA,客户端信任库只有根证书,中间证书必须由服务器发。
一句话记住它:服务器负责把整条链送到客户端手上,别指望对方替你跑腿。
延伸阅读
- Let's Encrypt 的链结构说明(了解根与中间证书的关系可参考其证书文档)
- Nginx HTTPS 服务器配置(官方关于证书文件与链的配置说明)