本质是TLS握手阶段SNI匹配失败:每个域名必须独占独立server块并精准配置ssl_certificate路径,共用块或证书不匹配会导致证书错配、请求进错块;需设default_server兜底并用openssl验证SNI生效。

HTTPS 下虚拟主机冲突,本质是 TLS 握手阶段的域名识别没对上——Nginx 用 SNI 匹配 server_name,但配置稍有偏差,就会让请求进错块、证书报错、甚至返回空白页。关键不在“有没有配多个 site”,而在“每个 HTTPS 块是否独立、精准、可区分”。
每个域名必须独占一个 server 块(不能塞一起)
Nginx 不支持在一个 server { listen 443 ssl; } 块里靠多个 server_name a.com b.com 来共用一套证书服务不同域名——除非你用的是同一张多域名(SAN)或通配符证书,且所有域名都明确列在该证书中。
- ✅ 正确:为
a.com单独写一个server块,指定它的证书路径;再为b.net单独写一个,指定它的证书路径 - ❌ 错误:把
server_name a.com b.net;写在同一块里,却只配了a.com的证书——b.net访问时会拿到a.com的证书,浏览器直接报“证书名称不匹配”
确保每个 listen 443 ssl 都带完整 SSL 上下文
哪怕只是测试,每个监听 443 的 server 块也必须包含:
-
ssl_certificate(公钥证书,含完整链) -
ssl_certificate_key(私钥,权限应为600,Nginx 主进程可读) - 推荐加上
ssl_trusted_certificate(用于 OCSP 装订)
漏掉任意一项,Nginx 启动可能不报错,但客户端发起 HTTPS 请求时会握手失败,表现为白屏、ERR_SSL_PROTOCOL_ERROR 或反复重定向。
显式声明 default_server,防意外接管
当用户用 IP 直连、Host 头为空、或输错域名访问 443 端口时,Nginx 会把请求交给该端口下的默认 server 块。如果这个块绑的是 a.com 的证书,那所有未命中域名都会触发证书错配警告。
建议单独建一个最小化兜底块:
server {
listen 443 ssl default_server;
ssl_certificate /etc/ssl/nginx/default.crt;
ssl_certificate_key /etc/ssl/nginx/default.key;
return 444; # 直接关闭连接,不返回任何响应
}这样既避免泄露主站信息,也防止错误证书污染信任链。
验证 SNI 是否真生效:用 curl 指定 Host 测试
别只用浏览器访问——浏览器会缓存、跳转、自动补 www,干扰判断。用命令行直击核心:
-
curl -I --resolve a.com:443:你的服务器IP https://a.com——看响应头和证书是否来自预期站点 -
openssl s_client -connect 你的IP:443 -servername b.net -showcerts 2>/dev/null | openssl x509 -noout -text | grep "Subject:"——确认实际返回的证书 Subject 是否含b.net
如果结果不符,说明 Nginx 没按你写的 server_name 分发,大概率是配置加载顺序、default_server 缺失,或 server_name 拼写/大小写/空格有误。


















