err_cert_common_name_invalid 根本原因是nginx未通过sni精准返回匹配域名的证书,需逐层验证实际响应证书、sni传递、server块独立性、san覆盖及默认块干扰。

问题核心在于:多个域名共用一个IP时,Nginx必须靠SNI精准选择对应证书;一旦SNI未生效、server块错配或证书SAN缺失,浏览器就会报ERR_CERT_COMMON_NAME_INVALID——这不是证书“坏了”,而是“发错了”。排查要从客户端看到的证书出发,逐层向上对齐。
确认实际返回的是哪张证书
别只看配置文件里写了什么,先验证真实响应:
- 用
curl -vkI https://your-domain.com查看实际返回的证书 subject 和 SAN,注意是否含你访问的域名 - 分别加
-4和-6测试 IPv4/IPv6 路径,避免 AAAA 记录指向了没同步证书的另一台服务器 - 若返回的证书是
www.example.com,但你访问的是api.example.com,说明请求被 fallback 到了默认 server 块
检查 SNI 是否被正确传递和识别
Nginx 依赖 SNI 决定用哪个 server 块,不带 SNI 就会取第一个 listen 443 ssl 的块:
- 用
openssl s_client -connect IP:443 -servername your-domain.com -showcerts模拟带 SNI 的握手,观察返回证书是否匹配 - 去掉
-servername参数再试一次:如果两次返回不同证书,说明 SNI 生效;如果相同,说明 Nginx 没按域名分发,可能 server_name 配置有误或存在隐式默认块 - 确保每个域名都有独立的
server { listen 443 ssl; server_name domain-a.com; },不要把多个不相关域名塞进同一个server_name列表
验证证书 SAN 是否覆盖所有访问路径
现代浏览器只认 SAN,CN(Common Name)已基本失效:
- 运行
openssl x509 -in fullchain.pem -text -noout | grep -A1 "Subject Alternative Name",确认输出中明确列出DNS:your-domain.com -
*.example.com不覆盖example.com或sub.api.example.com;跨主域(如site-a.com和site-b.net)必须用 SAN 证书,且全部域名写在同一张证书里 - Let’s Encrypt 用户请务必使用
fullchain.pem(证书+中间链),不能只放cert.pem,否则客户端无法构建信任链
排除默认 server 干扰和兜底行为
Nginx 总会把不匹配任何 server_name 的请求交给第一个 listen 443 ssl 的块:
- 检查配置中是否存在未注释的、监听 443 ssl 的“默认”server 块(例如只配了
server_name _;或空列表) - 生产环境建议显式定义一个兜底块:
server { listen 443 ssl default_server; return 444; },避免意外暴露测试证书 - 用
nginx -T | grep -A5 "listen 443 ssl"查看所有 HTTPS server 块的加载顺序,确认目标域名块没有被更早的块拦截











