错误源于证书的SAN未包含api.example.com,需用curl -vkI和openssl s_client -servername命令验证实际返回证书的subjectAltName是否匹配域名,同时检查Nginx server_name、ssl_certificate及代理SNI透传配置是否一致。

这个错误不是证书没装好,而是浏览器确认了“你访问的是 api.example.com”,但服务器返回的证书里压根没写这个域名。排查核心就一件事:搞清 实际返回的证书内容 和 你期望它覆盖的域名 是否一致。
用 curl 看真实返回的证书信息
别只信配置文件,直接看 Nginx 实际发出去的是什么:
-
curl -vkI https://api.example.com/ 2>&1 | grep -E "(subject|DNS)"—— 查看默认解析下返回证书的 subject 和 SAN 列表 -
curl -4vkI https://api.example.com/ 2>&1 | grep -E "(subject|verify)"—— 强制走 IPv4,排除 AAAA 记录指向旧服务器的干扰 -
curl -6vkI https://api.example.com/ 2>&1 | grep -E "(subject|verify)"—— 强制走 IPv6,验证是否因双栈导致证书不一致
重点看输出里 DNS:api.example.com 是否出现在 subjectAltName 行中。没有就说明证书本身不支持该域名。
用 openssl 模拟 TLS 握手并指定 SNI
Nginx 多站点靠 SNI 区分请求,不带 SNI 很可能落到默认 server 块,返回的是另一张证书:
-
openssl s_client -connect 203.0.113.10:443 -servername api.example.com -showcerts /null 2>/dev/null | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"—— 明确带上-servername,确保命中正确的 server 块 - 对比执行
openssl s_client -connect 203.0.113.10:443 -showcerts(不带 -servername)的结果,如果两者 SAN 不同,说明存在默认块劫持问题
检查 Nginx 配置是否精准对齐
三个地方必须同时满足,缺一不可:
-
server_name api.example.com;要和访问域名完全一致(区分 www/non-www、大小写) -
ssl_certificate指向的证书文件,其 SAN 字段必须包含api.example.com(仅靠 CN 已无效) - 若前端有 CDN 或反向代理,确保它透传了
Host头和 TLS 层的 SNI(部分四层代理会剥离 SNI)
自签名或内网 IP 场景要特别处理 SAN
用 https://192.168.3.71 这类 IP 访问时,证书必须显式声明 IP 地址在 SAN 中:
- 生成证书时不能只写
CN=192.168.3.71,必须加subjectAltName=IP:192.168.3.71 - 推荐用配置文件方式生成,避免漏掉扩展项;命令行方式需明确使用
-addext "subjectAltName=IP:192.168.3.71" - 证书生成后,用
openssl x509 -in server.crt -text -noout | grep -A1 "Subject Alternative Name"确认 IP 条目已写入


















