nginx stream模块不支持tls解密与卸载,仅能通过ssl_preread on预读sni/alpn实现四层动态分流,配合stream_ssl_module完成tls透传、重加密及路由,不触碰应用层数据。

Nginx 的 stream 模块本身不支持真正的 TLS 动态卸载——它无法解密 TLS 流量、验证证书、终止会话或读取 HTTP 头。所谓“TLS 卸载”,在四层语境中常被误用;实际能做的,是 TLS 透传 + SNI/ALPN 预读路由,配合 stream_ssl_module 实现基于加密握手信息的动态分流,而非七层意义上的“卸载”。
下面分三部分讲清楚怎么做、能做什么、不能做什么:
stream 模块实现 TLS 分流的核心能力
-
ssl_preread on:启用 SNI(Server Name Indication)和 ALPN(Application-Layer Protocol Negotiation)预读,仅解析 ClientHello 前几十字节,不建立 TLS 连接 -
map $ssl_preread_server_name $backend { ... }:根据域名做后端映射(如api.example.com → upstream_api) -
map $ssl_preread_alpn_protocols $proto_backend { ... }:根据 ALPN 协议名分流(如h2→ HTTP/2 后端,grpc→ gRPC 后端) -
proxy_ssl on+proxy_ssl_protocols:向上游发起 TLS 连接(即重加密),用于 mTLS 或后端强制 TLS 场景
这些操作全程不触碰应用层数据,也不解密 payload,因此低延迟、高并发,适合网关前置路由。
关键配置示例(SNI + ALPN 双维度分流)
stream {
# 启用 SNI 和 ALPN 预读
ssl_preread on;
# 按 SNI 域名映射逻辑上游
map $ssl_preread_server_name $sni_upstream {
hostnames;
app1.example.com upstream_app1;
app2.example.com upstream_app2;
default upstream_default;
}
# 按 ALPN 协议选择转发策略(需客户端支持)
map $ssl_preread_alpn_protocols $alpn_route {
~\bh2\b route_http2;
~\bgrpc\b route_grpc;
~\bhttp/1\.1\b route_http1;
default route_default;
}
upstream upstream_app1 {
server 10.0.1.10:443;
server 10.0.1.11:443;
}
upstream upstream_app2 {
server 10.0.1.20:443;
}
server {
listen 443 ssl preread;
proxy_pass $sni_upstream;
# 可选:向上游开启 TLS(重加密)
proxy_ssl on;
proxy_ssl_protocols TLSv1.2 TLSv1.3;
proxy_ssl_verify off;
}
}
⚠️ 注意:
listen 443 ssl preread中的ssl参数依赖ngx_stream_ssl_preread_module,不是用来终止 TLS,而是启用预读能力。
什么算“动态”?什么不能做?
- ✅ 动态体现在:无需重启 Nginx,修改
map规则或 upstream 列表即可生效(reload 即可) - ✅ 支持运行时按 SNI 域名、ALPN 协议、甚至客户端 IP(结合
$remote_addr)做条件路由 - ❌ 不支持基于 URL、HTTP 方法、Header、Cookie 等七层信息分流(那是
http模块的事) - ❌ 不支持真正 SSL 卸载后转成明文 HTTP 给后端(那需要
http { server { listen 443 ssl; ... } }) - ❌ 不支持对已加密流量做内容改写、重定向、WAF 检测等(无协议解析能力)
如果你需要的是“前端统一解密 TLS,再以 HTTP 明文转发给不同服务”,请用 http 模块 + ssl_certificate;
如果你要的是“同一 443 端口接入,自动把 a.com 流量导到 A 集群、b.com 导到 B 集群,且全程保持 TLS 加密”,stream 就是正确选择。
不复杂但容易忽略。











