关键是为每个域名配置独立server块,精确匹配server_name与专属证书路径,并确保客户端发送SNI、Nginx正确路由;错误做法是多域名共用一个server块或证书路径。

多域名共用一个 IP 地址时,Nginx 要让每个域名正确加载对应证书、不出现“证书错配”或“502/403”,关键不是堆证书,而是确保 SNI 在 TLS 握手阶段被客户端正确发送、Nginx 正确接收并精准路由到对应 server 块——任何一环断开,就会触发兼容性故障。
每个域名必须独占一个 server 块
不能把多个 server_name 写进同一个 server 块里再共用一套证书。Nginx 不会按域名动态切换证书;它只在握手初始阶段靠 SNI 字段查匹配的 server 块,然后加载该块内配置的 ssl_certificate 和 ssl_certificate_key。
- 正确写法:为 site-a.com、site-b.com 各自写一个独立的
server { listen 443 ssl; server_name site-a.com; ... }块 - 错误写法:一个块里写
server_name site-a.com site-b.com;,再配同一套证书——后端或浏览器看到的始终是第一个匹配的证书,SNI 失效 - 注意 default_server:未匹配到任何
server_name的请求(如直接用 IP 访问),会落到带default_server标记的块,它的证书就成了“兜底证书”,务必设为可信或明确提示
SNI 必须由客户端发起,且 Nginx 要能识别
Nginx 本身不生成 SNI,它只是 TLS 服务端,被动接收客户端(浏览器、APP、curl)在握手初期发来的 SNI 扩展字段。如果客户端不支持 SNI(如 Windows XP + IE6、极老 Android),Nginx 就只能返回 default_server 的证书,必然报错。
- 验证方法:
openssl s_client -connect your.com:443 -servername site-a.com -showcerts,看返回的证书是否与server_name site-a.com块中配置的一致 - 若去掉
-servername参数,返回的是另一个域名的证书,说明 SNI 未生效或客户端没发——此时应检查客户端环境,而非改 Nginx 配置 - Nginx 1.11.0+ 默认启用 SNI 支持,无需额外开关;旧版本需确认编译时含
--with-http_ssl_module
证书链要完整,且域名必须精确匹配 SNI 值
即使 SNI 正确送达,后端证书若不覆盖该域名,也会握手失败。常见问题包括:
- 泛域名证书(
*.example.com)不能匹配example.com(主域需单独签或用通配符+主域合并证书) - 证书 SAN(Subject Alternative Name)里漏了某个子域名,比如只写了
api.example.com,但请求的是www.example.com - 中间 CA 证书未随主证书一起下发:用
openssl s_client -connect your.com:443 -servername your.com -showcerts查看是否返回全部三级链(根→中间→站点) - 私钥与证书模值不一致:运行
openssl x509 -noout -modulus -in cert.pem | openssl md5和openssl rsa -noout -modulus -in key.pem | openssl md5,输出必须完全相同
避免 upstream 场景下的 SNI 二次干扰
如果你的 Nginx 是反向代理(即 proxy_pass https://backend),那它同时扮演两个角色:对外是 TLS 服务端(收客户端 SNI),对内是 TLS 客户端(向后端发 SNI)。这两层 SNI 独立,必须分别处理:
- 对外 SNI:靠
server_name+ 独立server块解决 - 对内 SNI:必须加
proxy_ssl_server_name on;和proxy_ssl_name "backend.example.com";(引号不能少),否则 Nginx 默认用proxy_pass中的域名/IP 当 SNI,后端无法选证 - 特别注意:若
proxy_pass https://10.0.0.5,即使配了proxy_ssl_name,也必须确保后端 HTTPS 服务监听该 IP 并支持对应域名的证书,否则仍失败


















