
SSL 证书链不完整,通常是服务器只发送了站点证书,却没有同时发送签发它的中间 CA 证书。部分桌面浏览器可能因为本地缓存过中间证书而仍能正常访问,但新设备、移动应用、API 客户端、Java 程序或严格验证证书链的监控工具可能出现“证书不受信任”“unable to get local issuer certificate”或“certificate verify failed”等错误。
修复的核心不是重新生成私钥,也不是把所有证书随意拼接,而是确认正确的信任路径,并按“站点证书在前、中间证书随后”的顺序向客户端发送完整链。一般不需要发送根证书,因为受信任根证书应已存在于客户端的信任库中。
本文配置核查日期为 2026 年 8 月 3 日。示例中的域名和文件路径均为占位符,使用前请替换为自己的配置,并先在测试环境验证。
什么是 SSL 证书链?
一个常见的 TLS 信任链包含三层:
站点证书,也称叶子证书:签发给
www.example.com等具体域名。中间 CA 证书:由根 CA 或更高层中间 CA 签发,用于签发站点证书。
根 CA 证书:位于操作系统、浏览器、Java 或应用自己的信任库中,是信任路径的锚点。
TLS 服务器通常应发送站点证书和必要的中间证书。客户端使用服务器发送的中间证书,把站点证书连接到本地已信任的根证书。
常见正确顺序:站点证书 → 签发站点证书的中间证书 → 更高层中间证书。通常不要把根证书附加到服务器证书链中。
证书链不完整有哪些典型表现?
浏览器显示证书正常,但某些手机、旧系统或应用无法连接。
curl返回SSL certificate problem: unable to get local issuer certificate。OpenSSL 显示
Verify return code: 20或21。Java、Python、Node.js 或其他 API 客户端提示证书验证失败。
CDN 回源 HTTPS 失败,表现为 502、525、526 或平台自定义的源站 TLS 错误。
证书检测工具提示 Chain issues、Incomplete chain 或 Extra download。
“在我的浏览器中正常”不能证明证书链配置正确。浏览器可能已经从之前访问、系统更新或 Authority Information Access 地址获取并缓存了中间证书,而新的客户端没有这些缓存。
第一步:用 OpenSSL 检查服务器实际发送的证书
使用 -servername 发送 SNI,避免多域名服务器返回默认证书:
openssl s_client \ -connect www.example.com:443 \ -servername www.example.com \ -showcerts \ -verify_return_error </dev/null
重点检查:
Certificate chain 中是否只有编号 0 的站点证书。
每一层证书的 Subject 是否能与上一层的 Issuer 对应。
最终的
Verify return code是否为0 (ok)。服务器返回的证书域名是否与测试域名匹配。
只使用 openssl s_client -connect IP:443 而不设置 SNI,可能得到另一个虚拟主机的证书,从而把域名配置错误误判为证书链问题。
如果已将证书保存到本地,还可以验证站点证书能否通过中间证书连接到受信任根:
openssl verify \ -CAfile root-ca.pem \ -untrusted intermediate-chain.pem \ server-certificate.pem
输出 server-certificate.pem: OK 表示给定的证书文件能够构建信任链。此命令只验证本地文件,不代表 Web 服务器已经发送了相同的中间证书,因此修改配置后仍应重新运行 s_client。
第二步:确认 fullchain.pem 的内容与顺序
一个用于服务器的完整链文件通常应采用以下排列:
-----BEGIN CERTIFICATE----- 站点证书内容 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- 直接签发站点证书的中间 CA 证书 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- 更高层中间 CA 证书(如适用) -----END CERTIFICATE-----
不要把私钥内容放入公开证书链文件。私钥应单独保存在权限受限的文件中,例如 privkey.pem。
如果证书机构提供了以下文件,通常可以这样理解:
cert.pem或域名命名的 CRT 文件:站点证书。chain.pem或 CA Bundle:一个或多个中间证书。fullchain.pem:站点证书加中间证书。privkey.pem:私钥,必须保密。
不同证书机构的文件命名并不完全一致,不能只凭文件名判断。可以使用下面的命令查看每张证书的 Subject、Issuer 和有效期:
openssl x509 -in certificate.pem \ -noout -subject -issuer -dates -serial
Nginx 如何配置完整证书链?
Nginx 的 ssl_certificate 文件应先包含站点证书,再包含中间证书。典型配置如下:
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
# 其他站点配置
}如果证书机构只提供独立文件,可按正确顺序生成 fullchain:
cat server-certificate.pem intermediate-chain.pem \ > fullchain.pem
不要反过来拼接。Nginx 官方文档说明,服务器证书必须位于组合文件的最前面,后面依次放置中间证书。如果顺序错误,Nginx 可能在启动时提示私钥与证书不匹配,因为它会尝试将私钥与文件中的第一张证书配对。
修改后先检查语法,再平滑重载:
nginx -t systemctl reload nginx
如果使用容器,应确认实际挂载到容器中的文件已经更新,而不是只替换宿主机上未被挂载的副本。还要检查是否有多个 Nginx 实例或负载均衡节点仍在使用旧证书。
Apache HTTP Server 如何配置完整证书链?
在 Apache HTTP Server 2.4.8 及更高版本中,SSLCertificateFile 可以包含站点证书和中间证书,顺序仍然是站点证书在前:
<VirtualHost *:443> ServerName www.example.com SSLEngine on SSLCertificateFile /etc/apache2/ssl/fullchain.pem SSLCertificateKeyFile /etc/apache2/ssl/privkey.pem </VirtualHost>
SSLCertificateChainFile 在 Apache 2.4.8 起已被弃用,因为中间证书可以直接包含在 SSLCertificateFile 中。维护旧系统时可能仍能看到独立链文件配置,但新配置应优先采用当前方式,并以实际安装版本的文档为准。
修改后可检查配置并优雅重载:
apachectl configtest apachectl graceful
在某些发行版中,命令可能是 apache2ctl 或通过 systemctl reload httpd 执行。重载后务必从外部重新检查服务器实际发送的证书链。
CDN 上如何配置第三方 SSL 证书链?
在 CDN 或云平台上传自有证书时,通常需要分别填写:
站点证书或 Certificate body。
私钥或 Private key。
中间证书链或 Certificate chain。
证书链字段一般只放中间证书,不放站点证书、私钥或根证书。中间证书应从直接签发站点证书的 CA 开始,按信任路径依次排列。
以 AWS Certificate Manager 导入证书为例,证书链不是必填字段,但如果站点证书不是由可直接信任的根签发,就必须提供中间证书链。AWS 文档要求链中的证书按顺序排列,并且不要包含根证书。
CloudFront 的查看器证书还有一个常见区域要求:用于 CloudFront 分配的 ACM 证书必须申请或导入到美国东部(弗吉尼亚北部)区域,即 us-east-1。证书位于其他区域时,不会出现在 CloudFront 的查看器证书选择列表中。
“CDN 边缘证书正常”与“CDN 回源证书正常”是两个独立问题。用户到 CDN 使用查看器证书,CDN 到源站使用源站证书。前者配置正确,源站链不完整时仍可能导致 CDN 回源 TLS 失败。
不要把根证书加入服务器证书链
根证书是客户端信任库中的信任锚。服务器发送根证书通常没有必要,因为客户端不会因为服务器主动发送某个根证书就自动信任它。附带根证书只会增加握手数据量,有时还可能导致平台导入校验失败。
正确目标不是“发送越多证书越好”,而是发送构建到客户端可信根所需的最短有效中间链。
常见配置错误
只配置了站点证书
这是最常见的原因。某些浏览器能自动补齐中间证书,但 API 客户端无法补齐,因此会出现设备间结果不一致。
证书链顺序颠倒
将中间证书放在站点证书前面,可能导致服务启动失败、私钥匹配错误或平台拒绝导入。
把私钥或根证书拼入 fullchain
私钥不属于证书链,泄露私钥会使证书失去安全性。根证书通常也不应由服务器发送。
站点证书与私钥不匹配
可以比较公钥摘要,而不是依赖已经不适用于所有算法的 RSA modulus 方法:
openssl x509 -in server-certificate.pem -pubkey -noout \ | openssl pkey -pubin -outform pem \ | sha256sum openssl pkey -in privkey.pem -pubout -outform pem \ | sha256sum
两条命令的摘要应一致。执行时不要把私钥内容上传到在线检测网站。
修改了错误的虚拟主机或节点
同一服务器可能有多个域名配置,同一网站也可能位于多个负载均衡节点。检查 SNI、DNS 解析和每个后端节点,避免只更新其中一台。
服务没有真正重载
替换文件后,如果 Nginx、Apache、负载均衡器或容器仍持有旧配置,外部看到的证书不会变化。应先执行语法检查,再平滑重载并复测。
完整的修复与验证流程
使用 OpenSSL 和 SNI 检查线上服务器实际发送的证书。
确认错误是缺少中间证书,而不是域名不匹配、证书过期或客户端信任库问题。
从证书机构的官方渠道获取正确的中间证书,不从不明网站下载。
检查站点证书的 Issuer,并建立到可信根的正确路径。
按“站点证书 → 中间证书”的顺序生成 fullchain,通常不包含根证书。
确认站点证书与私钥匹配。
更新 Nginx、Apache、负载均衡器或 CDN 的证书配置。
执行配置语法检查,并平滑重载服务。
从不同网络或未缓存中间证书的环境重新测试。
检查所有节点、IPv4 与 IPv6 地址、CDN 查看器链和回源链。
常见问题
fullchain.pem 包含私钥吗?
不包含。fullchain.pem 通常只包含站点证书和中间证书。私钥应保存在单独的 privkey.pem 或 key 文件中,并限制文件权限。
证书链中需要放根证书吗?
通常不需要。根证书应由客户端信任库提供。服务器只需发送站点证书和建立信任路径所需的中间证书。
为什么电脑浏览器正常,手机应用却报证书错误?
电脑可能缓存过中间证书,或使用了不同的信任库。手机应用和 API 客户端没有对应缓存时,就会暴露服务器链不完整的问题。
更换证书后为什么仍显示旧证书?
可能是服务未重载、请求命中了另一台节点、DNS 指向旧服务器、CDN 仍使用旧证书,或 IPv4 与 IPv6 后端配置不一致。使用 OpenSSL 分别连接具体 IP 并设置 SNI,可以进一步定位。
CDN 上传证书成功就代表源站证书也正确吗?
不代表。CDN 查看器证书负责用户到 CDN 的连接,源站证书负责 CDN 到源站的连接。两条 TLS 链需要分别验证。
可以从其他网站随便下载中间证书吗?
不建议。应从证书机构、ACM 或服务器自动化证书客户端的官方渠道获取,并核对证书的 Subject、Issuer、有效期和指纹。
结论
修复 SSL 证书链不完整的关键,是让服务器发送正确且有序的中间证书,而不是公开私钥、盲目加入根证书或重复申请证书。Nginx 的证书文件应以站点证书开头,随后放置中间证书;Apache 2.4.8 及以上可在 SSLCertificateFile 中使用同样的完整链;CDN 的证书链字段通常只填写按顺序排列的中间证书。
配置完成后,必须使用带 SNI 的 OpenSSL 命令从外部检查服务器实际发送的链,并同时验证 CDN 查看器连接和 CDN 回源连接。只有不同客户端都能构建到可信根的完整路径,证书链问题才算真正修复。
CloudFlew 提供 CDN 与 SSL 相关服务。部署证书时,应同时检查站点证书、私钥匹配、中间证书顺序、边缘证书和源站证书,避免只验证浏览器中的单次访问结果。
参考资料
NGINX 官方文档:Configuring HTTPS servers,核查日期:2026-08-03
Apache HTTP Server 官方文档:mod_ssl,核查日期:2026-08-03
OpenSSL 官方文档:openssl-s_client,核查日期:2026-08-03
OpenSSL 官方文档:openssl-verify,核查日期:2026-08-03
AWS Certificate Manager 用户指南:导入证书格式,核查日期:2026-08-03
AWS CloudFront 开发者指南:证书要求,核查日期:2026-08-03