证书链不完整导致部分手机打不开网站,根本原因是服务器在 TLS 握手时只下发了站点自己的证书(叶证书),没有一并下发中间证书;修复方法是把「叶证书 + 中间证书」按顺序拼成一个 fullchain 文件,在 Nginx 的 ssl_certificate 或 Apache 的 SSLCertificateChainFile 中指向它,重启服务后用 openssl s_client 复测确认返回 2 段以上证书。
为什么电脑浏览器能正常打开,手机却报证书错误?
结论:电脑浏览器会自动补齐缺失的中间证书,多数手机端 WebView 不会补齐;缺了中间证书,手机就直接判定证书不受信任。
证书链的结构是「根证书 → 中间证书 → 服务器证书(叶证书)」。浏览器和操作系统通常只内置根证书,中间证书必须由服务器在握手时一起发送。
Chrome、Edge、Firefox 在桌面端实现了 AIA(Authority Information Access,证书里记录中间证书下载地址的扩展字段)补全,发现链断了会自己去下载中间证书,所以你本地测试一切正常。而安卓旧版本的 OkHttp、系统 WebView、微信内置浏览器以及部分 App 的 HTTPS 实现,往往不做 AIA 抓取,只校验服务器发过来的那一段,链不完整就直接断开,表现为「无法连接」「证书无效」或白屏。
另一个高发场景是没有 SSL Labs 评分意识的运维直接用云厂商后台的「服务器证书」而不是「证书链」去部署。结论:单证书文件(只含一段 -----BEGIN CERTIFICATE-----)在移动端是不完整的部署。
怎么判断自己的网站是不是证书链不完整?
结论:一条命令就能确认——openssl s_client 输出里 BEGIN CERTIFICATE 出现次数少于 2 次,就是链不完整。
执行:
openssl s_client -connect www.example.com:443 -servername www.example.com -showcerts </dev/null
重点看两处:
- 输出中
-----BEGIN CERTIFICATE----- 的段数。正常应为 2 段(叶证书 + 中间证书),部分 CA 会发 3 段(中间证书分两级)。只有 1 段即判定不完整。
- 末尾的
Verify return code,正常是 0 (ok);若是 21 (unable to verify the first certificate),基本可以确诊。
另外两个交叉验证手段:
- 打开
https://www.ssllabs.com/ssltest/ 输入域名,结果页若出现 Chain issues: Incomplete,直接对应本问题。
- 在电脑浏览器点地址栏锁图标 → 证书 → 「证书路径」,如果中间证书图标带黄色感叹号或显示「缺少」,说明该节点没下发。
证书链不完整怎么修复?Nginx 和 Apache 分别怎么配?
结论:核心动作只有一个——把中间证书拼接到站点证书后面,让配置指向这个合并文件。
第一步,确认文件。CA 下发的压缩包里,Nginx 通常给的是 xxx_bundle.crt 或 fullchain.pem,里面已含中间证书;Apache 通常给 xxx.crt(叶证书)+ xxx_ca.crt(中间证书)两个文件。用 cat 拼接,顺序不能颠倒,叶证书必须在前:
cat your_domain.crt intermediate.crt > fullchain.crt
拼接后可用 openssl x509 -in fullchain.crt -noout -subject 验证第一段是不是你的域名。顺序反了(中间证书在前)在部分客户端同样会报错。
第二步,改配置。
Nginx:
ssl_certificate /etc/nginx/ssl/fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/private.key;
改完执行 nginx -t 检查语法,再 nginx -s reload 平滑重载。
Apache(2.4.8 之前):
SSLCertificateFile /etc/httpd/ssl/your_domain.crt
SSLCertificateChainFile /etc/httpd/ssl/intermediate.crt
SSLCertificateKeyFile /etc/httpd/ssl/private.key
Apache 2.4.8 及以后 SSLCertificateChainFile 已废弃,直接把中间证书追加到 SSLCertificateFile 指向的文件里即可。
第三步,如果前面挂了 CDN(Cloudflare、阿里云 CDN、腾讯云 CDN 等),链不完整通常出在 CDN 的证书上传环节:多数 CDN 要求「证书内容」和「证书链」分两个输入框填写,只传了前者就会出现本文的症状,需要在控制台补全中间证书并刷新部署。
修复后怎么验证真的生效了?
结论:必须用移动网络实测,本地 WiFi + 电脑浏览器复测通过不代表修复完成。
按顺序做三件事:
- 重跑
openssl s_client ... -showcerts,确认输出 2 段以上证书且 Verify return code: 0 (ok)。
- 用手机断开 WiFi、切 4G/5G 流量打开站点,同时用微信内置浏览器打开一次(微信 WebView 校验较严,是最实用的移动端试金石)。
- 回 SSL Labs 复测,
Chain issues 一栏应消失,总分通常能到 A 或 A+。
还有哪些容易踩的坑?
结论:证书链补全后仍有旧机型打不开,多半是根证书信任问题,而非链不完整,两者要对症。
- 2024 年 9 月 30 日之后,Let's Encrypt 停止提供 DST Root CA X3 交叉签名,安卓 7.1.1 以下设备不再内置信任 ISRG Root X1,这类设备打不开是系统根证书太旧,换任何链都无效,只能引导用户升级系统或改用其他 CA 的证书。
- 不要把根证书放到
fullchain.crt 里,根证书客户端本来就有,多发只会增加握手包体积,个别客户端还会判为异常。
- OCSP Stapling 配置依赖中间证书,链不完整时 stapling 会直接失效,补全后记得
ssl_stapling on; 一起复查。
- 多域名 SNI 场景下,每个
server 块都要单独指向各自的 fullchain,别共用一个只含单证书的文件。
总结一句:证书链问题的定位成本极低(一条 openssl s_client 命令),修复成本也极低(拼接文件 + 改一行配置 + reload),真正耗时的是「以为电脑能打开就没问题」这个误判。上线任何 HTTPS 站点后,都要用手机流量和微信各打开一次,才算验收完成。