Nginx可通过stream上下文+ssl_preread模块实现基于SNI的动态HTTPS后端转发,需OpenSSL≥1.1.1、Nginx≥1.19.4并启用ngx_stream_ssl_preread_module,利用$ssl_preread_server_name变量路由至不同上游。

要实现基于SNI的动态后端HTTPS转发,Nginx本身不直接解析SNI并据此选择后端,但可通过结合OpenSSL 1.1.1+、Nginx 1.19.4+ 的 ssl_preread 模块与变量提取能力,在TLS握手阶段读取SNI,并用作反向代理决策依据——关键在于启用stream上下文的四层代理,而非http上下文。
前提:确认环境支持SNI预读
Nginx必须启用ngx_stream_ssl_preread_module(默认静态编译进官方主线版≥1.19.4;若为旧版或自编译,需确认含--with-stream_ssl_preread_module)。运行以下命令验证:
nginx -V 2>&1 | grep -o with-stream-ssl-preread-module
若无输出,需升级或重编译Nginx。同时确保系统OpenSSL ≥ 1.1.1(SNI预读依赖ALPN/SNI扩展解析)。
配置stream块提取SNI并路由到不同后端
在nginx.conf顶层(不在http块内)添加stream块,利用$ssl_preread_server_name变量做匹配:
stream {
upstream backend_app1 {
server 10.0.1.10:443;
}
upstream backend_app2 {
server 10.0.1.20:443;
}
<pre class='brush:php;toolbar:false;'>map $ssl_preread_server_name $upstream_backend {
hostnames;
app1.example.com backend_app1;
app2.example.com backend_app2;
default backend_app1;
}
server {
listen 443 ssl preread;
ssl_preread on;
proxy_pass $upstream_backend;
proxy_ssl on;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_ssl_verify off; # 若后端证书非公信CA签发,暂禁用校验(生产建议配trusted_certificate)
proxy_ssl_server_name on; # 将原始SNI透传给后端
}}
- listen 443 ssl preread:启用TLS预读监听,不终止SSL,仅提取SNI
-
proxy_ssl_server_name on:把客户端SNI值作为
server_name发给后端,便于后端(如另一台Nginx或应用)做二次SNI路由 -
proxy_ssl_verify off:开发调试可用;生产环境应设为on并指定
proxy_ssl_trusted_certificate
后端服务需支持SNI响应(常见场景说明)
该方案本质是四层代理,后端必须能独立处理HTTPS流量:
- 若后端是另一台Nginx:需配置
server { server_name app1.example.com; ssl_certificate ... },并开启ssl_protocols和对应证书 - 若后端是gRPC/Node.js/Go等应用:需自行实现TLS监听并根据SNI返回对应证书(如Go用
tls.Config.GetCertificate回调) - 不支持SNI的后端(如老版本Apache未配
NameVirtualHost)将无法区分域名,所有请求都走默认证书
验证与排错要点
用openssl s_client测试SNI是否被正确传递:
openssl s_client -connect your-nginx-ip:443 -servername app1.example.com -showcerts 2>/dev/null | openssl x509 -noout -text | grep "Subject.*CN\|DNS"
若返回证书CN为app1.example.com,说明SNI已成功路由至对应后端。常见问题:
-
SNI为空:客户端未发送SNI(如极老curl),或Nginx未启用
ssl_preread on -
502 Bad Gateway:后端不可达、证书不匹配、或
proxy_ssl_verify on但未配可信CA - 证书警告:后端返回了默认证书(非SNI匹配证书),检查后端SNI配置是否生效

















