nginx证书链缺失的根源是ssl_certificate文件未包含域名证书+中间证书,需用openssl s_client验证输出至少两段subject(含cn=域名和cn=中间ca),且fullchain.pem中域名证书在前、中间证书紧随、无根证书、无空行。

直接用命令验证服务端实际发送的证书内容,别依赖浏览器图标或页面提示。核心问题是 Nginx 没在 TLS 握手时把中间证书一起发出去,只发了域名证书,导致部分客户端(尤其是 iOS、安卓、Python requests、Java 程序)校验失败,报“不安全”“无法验证服务器身份”“CERTIFICATE_VERIFY_FAILED”等错误。
快速确认是否真缺中间证书
在服务器上运行:
openssl s_client -connect your-domain.com:443 -servername your-domain.com -showcerts 2>/dev/null | grep "subject="观察输出:
- 正常应有至少两行:第一行含 CN=your-domain.com(你的域名证书),第二行含中间 CA 名称,如 CN=Let's Encrypt R3 或 CN=DigiCert TLS RSA SHA256 2020 CA1
- 如果只有一行,或第二行是 CN=ISRG Root X1(根证书),说明链缺失、顺序错或混入了根证书
检查 fullchain.pem 文件是否拼对
Nginx 的 ssl_certificate 必须指向一个已拼好的 PEM 文件,不是单独的 cert.pem:
- 执行 cat /path/to/fullchain.pem,确认 BEGIN/END CERTIFICATE 块数量 ≥2
- 第一块必须是你域名的证书(Subject 含你的域名)
- 第二块起必须是中间证书,不能是根证书;多级中间证书需按信任路径从近到远排列(例如 R3 → E1 是对的,R3 → ISRG Root X1 是错的)
- 两段之间不能有空行,不能混入私钥、注释或 Windows 编码乱码
验证配置是否真正生效
改完配置 reload 后,别急着开浏览器:
- 重跑上面的 openssl s_client 命令,确认输出中明确出现两段证书,且第二段 Subject 显示中间 CA 名称
- 用 SSL Labs 测试,看 “Certificate Chain” 是否标绿、“Chain issues” 是否为 None
- 若仍报错,检查是否被 CDN 或浏览器缓存干扰:用 curl -v 直连 IP + 端口,或清 Safari/Chrome 的 HSTS 缓存(iOS:设置 → Safari → 清除历史记录和网站数据;macOS:Safari → 设置 → 隐私 → 管理网站数据 → 搜索并移除域名;Chrome:chrome://net-internals/#hsts)
避开两个高频误区
ssl_trusted_certificate 不是用来补全服务端证书链的——它只用于客户端证书验证或反向代理校验上游证书,设了对浏览器访问毫无帮助。
宝塔等面板的“自动补全证书链”功能不可靠:对 Let’s Encrypt 通常有效,但对 ZeroSSL、Sectigo、DigiCert 或自签名证书大概率失效,必须手动拼接验证。











