Nginx单IP多HTTPS站点依赖SNI技术,在TLS握手初期通过-servername字段匹配server_name与对应ssl_certificate,各server块须独立配置域名、证书路径及listen 443 ssl,无SNI请求则回落至首个或default_server块。

要让 Nginx 在单个 IP 上为不同域名自动匹配对应 HTTPS 证书,关键在于每个 server 块独立、精准地绑定域名与证书,并依赖 TLS 握手初期的 SNI 字段完成切换。这不是靠全局配置或条件判断,而是由 Nginx 内置机制在 SSL 握手阶段直接完成的。
每个 server 块必须完整独立
不能复用 ssl_certificate 指令,也不能把多个域名塞进同一个 server 块里“凑合用”一张证书(除非是 SAN 或泛域名证书)。正确做法是:每个主域名单独一个 server 块,且都监听 443 端口并启用 ssl:
-
listen 443 ssl http2;—— 必须带ssl参数,否则不加载证书 -
server_name site-a.com www.site-a.com;—— 必须精确匹配证书中 Subject Alternative Name(SAN)所含域名 ssl_certificate /etc/letsencrypt/live/site-a.com/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/site-a.com/privkey.pem;
证书路径必须一一对应
证书不是“共享资源”,Nginx 启动时不会报错,但只会实际加载第一个 server 块里的证书作为 fallback。后续块若证书路径写错、权限不足或文件不存在,会导致该域名 HTTPS 请求失败(如证书不匹配、连接重置):
- 推荐用 certbot 分别申请:
certbot --nginx -d site-a.com和certbot --nginx -d site-b.net - 证书目录建议按域名隔离,避免路径混淆,例如:
/etc/nginx/ssl/site-a.com/和/etc/nginx/ssl/site-b.net/ - 泛域名证书(
*.example.com)可覆盖子域,但无法用于other.net这类无关主域
处理无 SNI 的请求
老旧客户端(如 Android 4.4 以下、某些嵌入式设备)可能不发送 SNI 字段,此时 Nginx 会默认使用配置文件中第一个定义的 server 块的证书响应:
- 把最常用或默认站点放在最前面,降低误配概率
- 也可显式标记:
listen 443 ssl http2 default_server;,让该块明确承担兜底角色 - 注意:default_server 只影响无 SNI 请求,不影响正常 SNI 匹配逻辑
验证是否生效(不依赖浏览器)
用 OpenSSL 直接测试 TLS 层行为,绕过缓存和中间件干扰:
-
openssl s_client -connect your.ip:443 -servername site-a.com -showcerts 2>/dev/null | openssl x509 -noout -subject→ 应显示CN = site-a.com - 换
-servername site-b.net→ 应返回另一张证书的 subject - 省略
-servername参数 → 应返回第一个 server 块的证书










