nginx

关注公众号 jb51net

关闭
首页 > 网站技巧 > 服务器 > nginx > Nginx proxy_ssl_server_name 多域名匹配

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 判断返回哪张证书:

如果 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_certificateCA 证书路径用于验证后端返回的证书
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 多域名匹配内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文