Nginx proxy_ssl_server_name 解决后端多域名证书匹配失败的方法
作者:冷漠man
本文介绍了Nginx反向代理HTTPS后端时的关键指令proxy_ssl_server_name,当后端托管多域名证书时,Nginx需启用SNI扩展以正确匹配证书,本文详细分析了问题场景、基础解决方案,并提供了进阶配置方案,感兴趣的可以了解一下
proxy_ssl_server_name 是 Nginx 反向代理 HTTPS 后端时的关键指令,用于启用 SNI(Server Name Indication) 扩展,解决后端多域名虚拟主机场景下的证书匹配失败问题。
问题场景
当 Nginx 作为反向代理向后端 HTTPS 服务器发起连接时:
location / {
proxy_pass https://10.0.0.5; # 后端是 IP 地址
}后端服务器托管了多个域名证书(虚拟主机),依赖 SNI 判断返回哪张证书:
- 收到 SNI =
api.internal.com→ 返回 A 证书 - 收到 SNI =
svc.internal.com→ 返回 B 证书 - 无 SNI → 返回默认证书(可能是自签名或通配符证书)
如果 Nginx 不发送 SNI,后端可能返回错误的证书,导致 Nginx 验证失败:
SSL: error:0A000086:SSL routines::certificate verify failed
基础解决方案
location / {
proxy_pass https://backend.example.com;
# 核心:启用 SNI,发送 Host 头作为服务器名
proxy_ssl_server_name on;
# 验证后端证书(生产环境必须开启)
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/certs/ca.crt;
# 发送的 SNI 名称(默认使用 proxy_pass 中的主机名)
proxy_ssl_name backend.example.com;
}进阶配置方案
1. 变量动态 SNI(多租户/动态上游)
http {
# 根据请求特征动态选择后端 SNI
map $host $backend_sni {
default "api.internal.com";
"client-a.example.com" "a-api.internal.com";
"client-b.example.com" "b-api.internal.com";
}
server {
location / {
proxy_pass https://$backend_sni;
# 启用 SNI 并使用变量指定名称
proxy_ssl_server_name on;
proxy_ssl_name $backend_sni;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/certs/ca-chain.crt;
}
}
}2. IP 地址上游 + 显式 SNI 名称
upstream backend_nodes {
server 10.0.1.10:443;
server 10.0.1.11:443;
}
server {
location / {
proxy_pass https://backend_nodes;
# 必须使用 proxy_ssl_name 指定证书中的 CN/SAN
proxy_ssl_server_name on;
proxy_ssl_name api.internal.example.com;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/certs/internal-ca.crt;
# 透传原始 Host(与 SNI 分离)
proxy_set_header Host $host;
}
}3. 分层证书验证策略
server {
location /api/ {
proxy_pass https://api.internal:443;
proxy_ssl_server_name on;
proxy_ssl_name api.internal;
# 严格验证 + 深度
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
proxy_ssl_trusted_certificate /etc/nginx/certs/ca-bundle.crt;
# 会话复用优化
proxy_ssl_session_reuse on;
}
location /legacy/ {
proxy_pass https://old.internal:443;
proxy_ssl_server_name on;
proxy_ssl_name old.internal;
# 旧系统可能证书过期,临时放宽(不推荐长期)
# proxy_ssl_verify off;
}
}4. 与 resolver 配合的动态后端
server {
resolver 10.0.0.2 valid=10s;
location / {
set $target "service.consul";
proxy_pass https://$target:443;
# 动态解析时必须显式设置 SNI
proxy_ssl_server_name on;
proxy_ssl_name service.consul;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/nginx/certs/consul-ca.crt;
}
}关键指令关联
| 指令 | 作用 | 与 proxy_ssl_server_name 关系 |
|---|---|---|
| proxy_ssl_server_name | 启用 TLS SNI 扩展 | 主开关,默认 off |
| proxy_ssl_name | 指定发送的 SNI 主机名 | 默认使用 proxy_pass URL 中的主机名 |
| proxy_ssl_verify | 验证后端证书链 | 依赖正确的 SNI 获取对应证书 |
| proxy_ssl_trusted_certificate | CA 证书路径 | 用于验证后端返回的证书 |
| proxy_ssl_verify_depth | 验证链深度 | 防止中间人攻击 |
常见错误排查
错误 1:upstream SSL certificate does not match
# ❌ 错误:IP 代理无 SNI,后端返回默认证书 proxy_pass https://10.0.0.5; # ✅ 修正:启用 SNI 并指定期望的证书域名 proxy_ssl_server_name on; proxy_ssl_name api.example.com; proxy_pass https://10.0.0.5;
错误 2:SSL routines::shutdown while in init
后端要求 SNI 但未发送,或 TLS 版本不匹配:
proxy_ssl_server_name on; proxy_ssl_protocols TLSv1.2 TLSv1.3; proxy_ssl_ciphers HIGH:!aNULL:!MD5;
错误 3:通配符证书匹配失败
# 后端证书为 *.example.com proxy_ssl_server_name on; proxy_ssl_name api.example.com; # 正确:具体子域名 # proxy_ssl_name *.example.com; # 错误:SNI 不支持通配符写法
调试配置
server {
location / {
proxy_pass https://backend;
proxy_ssl_server_name on;
proxy_ssl_name backend.example.com;
# 临时开启详细日志(需编译时启用 debug)
error_log /var/log/nginx/debug.log debug;
# 添加响应头确认后端信息(调试用)
add_header X-Debug-Upstream $upstream_addr;
add_header X-Debug-Ssl-Name $proxy_ssl_name;
}
}完整生产模板
upstream api_backend {
server 10.0.2.10:443 weight=5;
server 10.0.2.11:443 weight=5;
keepalive 100;
}
server {
listen 443 ssl http2;
server_name gateway.example.com;
# 客户端到 Nginx 的证书
ssl_certificate /etc/nginx/certs/gateway.crt;
ssl_certificate_key /etc/nginx/certs/gateway.key;
location /api/ {
proxy_pass https://api_backend;
# === SNI 核心配置 ===
proxy_ssl_server_name on;
proxy_ssl_name api.internal.example.com;
# 证书验证
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
proxy_ssl_trusted_certificate /etc/nginx/certs/internal-ca-bundle.crt;
# 连接优化
proxy_ssl_session_reuse on;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
# 标准代理头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Request-ID $request_id;
}
}核心要点:当 proxy_pass 使用 IP 地址或上游组(upstream)时,必须显式设置 proxy_ssl_server_name on + proxy_ssl_name,确保后端能选择正确的虚拟主机证书完成 TLS 握手。
到此这篇关于Nginx proxy_ssl_server_name 解决后端多域名证书匹配失败的方法的文章就介绍到这了,更多相关Nginx proxy_ssl_server_name 多域名匹配内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
