nginx本身无需手动“开启sni”,只要编译时链接支持sni的openssl(≥0.9.8j)且启用--with-http_ssl_module,sni即自动生效;常见失效主因是openssl版本过低、不支持sni或被boringssl替代,而非配置错误。

结论很直接:Nginx 本身不需要“开启 SNI”,只要编译时链接了支持 SNI 的 OpenSSL(≥0.9.8j),且配置写对,SNI 就自动生效;所谓“不支持”,90% 是 OpenSSL 版本不对、或 Nginx 编译参数漏掉 --with-http_ssl_module,不是配置问题。
验证 OpenSSL 是否真正支持 SNI
很多人卡在这一步却以为是 Nginx 配置错了。运行:
nginx -V 2>&1 | grep -i openssl
必须看到类似 OpenSSL 1.1.1w 的输出,且不能是 BoringSSL。若输出为空、或显示 OpenSSL 0.9.8e、或带 TLS SNI support disabled,说明底层不满足条件。
- CentOS 6/7 系统自带 OpenSSL 多数够用,但 CentOS 5 或某些精简版镜像可能仍为 0.9.8e,必须升级 OpenSSL 并重新编译 Nginx
- Ubuntu/Debian 官方包通常没问题,但自建源或第三方仓库(如 nginx.org 的 .deb)若链接 BoringSSL,则 SNI 会失效
- 用
openssl s_client -connect your.ip:443 -servername example.com测试时,若返回证书主体不是example.com,而是另一个域名的证书,基本可断定是 OpenSSL 层未识别 SNI
每个 server 块必须独立绑定证书路径
这是最常被忽略的实操坑:Nginx 允许你在多个 server 块里写相同的 ssl_certificate 路径,启动也不报错,但运行时只加载第一个块的证书,其余全 fallback —— 表现就是所有域名都显示同一张证书。
-
server_name必须精确匹配你申请证书的域名,例如证书是site-a.com,就写server_name site-a.com;,不要加www.site-a.com除非证书也覆盖它 -
ssl_certificate和ssl_certificate_key必须指向该域名专属路径,比如/etc/letsencrypt/live/site-a.com/fullchain.pem,不能共用/etc/letsencrypt/live/common.pem - 用 certbot 时,务必分开执行:
certbot --nginx -d site-a.com和certbot --nginx -d site-b.net,不要一次写多个-d混在一起,否则生成的是多域名证书,无法实现真正的“按域名选证”隔离
处理无 SNI 请求的兜底逻辑
老旧设备(如 Windows XP + IE6)、某些 IoT 设备、或用 curl https://ip 不加 -servername 时,不会发送 SNI 字段。此时 Nginx 会把请求交给监听 443 端口的第一个 server 块处理,无论 server_name 是什么。
- 若你希望这类请求不触发证书警告,可显式定义一个
default_server块,并配一张通用兜底证书(比如 Let’s Encrypt 的泛域名证书或专用default.example.com) - 更安全的做法是直接拒绝:
return 444;(Nginx 特有关闭连接码),避免暴露任何证书信息 - 不要在
default_server块里复用其他站点的证书——浏览器会提示“证书与域名不匹配”,用户感知明显
真正难的不是写配置,而是确认 OpenSSL 是否如实参与 TLS 握手;很多线上环境反复 reload 配置却无效,根源都在 nginx -V 输出那行字没盯住。











