浏览器能开、curl 却报证书错:说说中间证书和证书链这回事

解释 TLS 中证书链与中间证书的概念,说明为什么服务器少发一张中间证书时浏览器往往正常、命令行和 API 调用却报错,以及如何排查和修复。

本文目录
浏览器能开、curl 却报证书错:说说中间证书和证书链这回事

线上有个经典场面:网站在 Chrome、Edge 里打开一切正常,锁头绿着;同事用 curl 一调,直接 unable to get local issuer certificate。于是怀疑客户端环境有问题,折腾半天 CA 包,最后发现锅其实在服务端——证书没配全。

这就是“证书链”这个概念最容易踩到的地方。

链是怎么回事

你拿到的站点证书(叶子证书)几乎从来不是根 CA 直接签的。中间隔着一层或多层中间 CA:根 CA 签中间 CA,中间 CA 签你的站点证书。验证时,客户端从叶子证书出发,顺着签名一层层往上找,直到碰到自己信任库里已有的根。中间任何一环缺失,链就断了。

关键点在于:客户端信任库里只有根证书,没有中间证书。所以中间证书必须由服务器在 TLS 握手时一起发出来,让客户端拿去拼图。

为什么浏览器没事,curl 有事

浏览器能开、curl 却报证书错:说说中间证书和证书链这回事:an ACME request being validated at separate stations and turned into a new deployable artifact while private key material remains at its origin
浏览器能开、curl 却报证书错:说说中间证书和证书链这回事:an ACME request being validated at separate stations and turned into a new deployable artifact while private key material remains at its origin查看原图

主流浏览器会做缓存和补链。它访问过别的站点、见过同一张中间证书,就缓存下来;你的服务器没发,它也能从缓存里翻出来补上。有些浏览器还会根据证书里的 AIA(Authority Information Access)扩展去在线下载缺失的中间证书。

curl、Java 应用、Python 的 requests、很多 IoT 设备和 API 网关没这么勤快。它们只信任本地 CA 包里的根,服务器给什么就用什么。少一张中间证书,链拼不上,直接报错。

所以“浏览器正常”从来不是证书配置正确的证据。手机 App、支付回调、监控系统这些不带缓存补链能力的客户端,才是照妖镜。

怎么确认和修复

最快的检查方式:

openssl s_client -connect example.com:443 -servername example.com
浏览器能开、curl 却报证书错:说说中间证书和证书链这回事:a new certificate version moving from its source to several edge appliances while the previous working version remains ready for rollback
浏览器能开、curl 却报证书错:说说中间证书和证书链这回事:a new certificate version moving from its source to several edge appliances while the previous working version remains ready for rollback查看原图

看输出里的 Certificate chain 部分,数一下服务器实际发了几张证书。只有一张(叶子自己),基本就是漏了中间证书。也可以用 SSL Labs 的在线检测,它会明确指出链不完整("Chain issues: Incomplete")。

修复方式取决于 Web 服务器。以 Nginx 为例,ssl_certificate 指向的文件需要包含完整链:叶子证书在前,中间证书依次拼接在后。CA 或 ACME 客户端签发的 fullchain.pem 就是干这个用的——很多人错把 cert.pem(只含叶子)填进去,于是出现“浏览器能用、接口全挂”的局面。

在 CertbotX 中实践

CertbotX 通过 ACME 自动签发证书时,使用的就是 CA 返回的完整链(fullchain),部署到 Agent、EdgeOne 或 CDN 时会带上中间证书,避免只推叶子证书导致部分客户端验证失败。每次签发和部署都有记录可查,如果某个端点出现链不完整的报错,可以对照部署记录确认该端点实际下发的证书内容。到期前自动重新签发也沿用同样的完整链,不会出现续期后链路变化的情况。

相关文档

← 返回资讯中心