随着网络环境对安全性要求的不断提升,支持安全通信的四层代理已成为必备能力。Nginx 的 Stream 模块不仅能处理裸 TCP 连接,还能通过 SSL 模块处理 TLS/SSL 加密流,或通过 SSL 预读模块在不中断加密的情况下获取关键信息,从而实现灵活的流量路由。本文将深入解析这两个核心模块的指令与实践。
SSL 阶段
在 TCP 四层代理场景中,直接透传加密流量或对流量进行加解密转换是常见需求。Nginx Stream 模块能够处理来自下游客户端的 TLS/SSL 加密连接,并根据需要将其转换为裸 TCP 流转发至上游服务器。
使用 TLS/SSL 主要有以下三种典型应用场景,下图清晰地展示了它们的数据流差异:

- 场景1:透传 SSL 流。Nginx 不解析 SSL,直接将客户端发来的加密 TCP 流原样转发给上游服务器。
- 场景2:剥离 SSL 层(重点)。Nginx 解密客户端发来的 TLS 协议,将其转换为明文 TCP 协议后再发送至上游服务。
- 场景3:后端加密。客户端与 Nginx 之间使用明文 TCP,但 Nginx 向上游服务发起连接时启用 TLS/SSL 加密。
ssl 模块详解
ngx_stream_ssl_module 模块使 Stream 反向代理能够处理下游客户端的 TLS/SSL 协议。该模块默认未被编译进 Nginx,需要通过 --with-stream_ssl_module 参数显式启用。
Stream SSL 模块的指令与 HTTP 模块中的 SSL 指令高度相似,以下列出部分常用指令及其配置示例。
stream {
map $ssl_alpn_protocol $proxy {
h2 127.0.0.1:8001;
http/1.1 127.0.0.1:8002;
}
# 全局配置,例如私钥密码文件
ssl_password_file /etc/keys/global.pass;
server {
listen 12346 ssl;
proxy_pass $proxy;
# 支持的 ALPN 协议列表
ssl_alpn h2 http/1.1;
# 支持多证书(RSA 和 ECDSA)
ssl_certificate example.com.rsa.crt;
ssl_certificate_key example.com.rsa.key;
ssl_certificate example.com.ecdsa.crt;
ssl_certificate_key example.com.ecdsa.key;
# 私钥密码文件
ssl_password_file global.pass;
# 支持的协议版本
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
# 支持的加密套件
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE:ECDH:AES:HIGH:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:!NULL:!aNULL:!eNULL:!EXPORT:!PSK:!ADH:!DH:!DES:!MD5:!RC4;
# SSL 握手超时时间(Stream 模块特有)
ssl_handshake_timeout 10s;
}
}
以下是 Stream SSL 与 HTTP SSL 常用指令的快速对照表,方便你理解:

核心指令速览
ssl_alpn:声明服务器支持的 ALPN 协议列表。
Syntax: ssl_alpn protocol ...;
Default: —
Context: stream, server
ssl_certificate 与 ssl_certificate_key:指定服务器证书和私钥路径。
Syntax: ssl_certificate file;
Syntax: ssl_certificate_key file;
Default: —
Context: stream, server
ssl_ciphers:指定支持的加密套件列表。
Syntax: ssl_ciphers ciphers;
Default: ssl_ciphers HIGH:!aNULL:!MD5;
Context: stream, server
ssl_protocols:指定支持的 TLS/SSL 协议版本。
Syntax: ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3];
Default: ssl_protocols TLSv1.2 TLSv1.3;
Context: stream, server
ssl_handshake_timeout:设置 SSL 握手超时时间,默认 60 秒。
Syntax: ssl_handshake_timeout time;
Default: ssl_handshake_timeout 60s;
Context: stream, server
此外,Stream SSL 模块还提供了丰富的内建变量,用于获取连接的安全信息,主要分为以下几类:
安全套件与协议信息
$ssl_cipher:本次连接使用的加密套件。
$ssl_protocol:使用的 TLS 协议版本(如 TLSv1.2)。
$ssl_alpn_protocol:协商的 ALPN 协议(如 h2)。
$ssl_session_reused:会话是否复用(“r”表示复用,“.”表示新建)。
客户端证书信息
$ssl_client_s_dn:客户端证书主题信息。
$ssl_client_i_dn:客户端证书颁发者信息。
$ssl_client_verify:客户端证书验证结果。
$ssl_client_v_remain:客户端证书剩余有效天数。
实践:SSL 剥离与 TCP 转发
本示例展示 Nginx 作为四层反向代理,将客户端的 SSL 连接解密后,以明文 TCP 形式转发至上游 HTTP 服务器。请注意,Stream 模块仅处理 TCP 和 SSL 层,不解析上层的 HTTP 协议。
步骤1:编译启用 Stream SSL 模块
./configure --prefix=/usr/local/nginx --with-stream --with-stream_ssl_module
make -j $(nproc) && make install
步骤2:配置 Nginx
-
Stream 代理服务器配置 (192.168.10.2):
worker_processes auto;
events {
worker_connections 1024;
}
stream {
log_format basic '$remote_addr:$remote_port "$realip_remote_addr:$realip_remote_port" [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time $ssl_protocol $ssl_session_reused';
access_log /var/log/nginx/stream_access.log basic;
server {
listen 8443 ssl; # 监听 SSL 端口
set_real_ip_from 10.20.172.0/24;
# SSL 证书配置
ssl_certificate /usr/local/nginx/certs/server.crt;
ssl_certificate_key /usr/local/nginx/certs/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5:!SHA1;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 转发至上游裸 TCP 服务
proxy_pass 10.20.172.213:8011;
}
}
-
上游 HTTP 服务器配置 (192.168.10.3):
server {
listen 8011; # 监听明文 TCP 端口
server_name _;
root /usr/local/nginx/html;
index index.html;
location /api {
return 200 '$time_iso8601 $request_id\n';
}
}
步骤3:验证测试
启动服务后,通过浏览器访问 https://server.weiyigeek.top:8443/index.html。同时在上游服务器抓包,可以观察到 TCP 三次握手后直接是明文的 HTTP 请求,证实 SSL 已被 Nginx 剥离。

步骤4:查看日志
检查 Stream 访问日志,$ssl_session_reused 变量可以显示会话复用情况(. 为新建,r 为复用)。
tail -f /var/log/nginx/stream_access.log
# 示例输出:... TLSv1.3 . # 新建连接
# 示例输出:... TLSv1.3 r # 复用连接
查看上游服务器的访问日志,确认请求已成功转发。

至此,我们完成了 SSL 解密并转发裸 TCP 流的常见四层代理模式。
PREREAD 阶段
在某些场景下,需要在保持 SSL 加密流完整(不解密)的前提下,获取客户端信息以进行路由决策。例如,根据客户端 TLS 握手信息中的 SNI 域名,将加密流量转发至不同的上游服务器。这正是 ngx_stream_ssl_preread_module 模块的用武之地。

ssl_preread 模块详解
stream_ssl_preread_module 模块默认未编译,需通过 --with-stream_ssl_preread_module 启用。它能够在 SSL 握手初期预读 Client Hello 消息,提取其中的 TLS 版本、SNI 域名和 ALPN 协议等信息,而不进行解密,随后将完整的加密流转发至上游。
核心指令
关键变量
$ssl_preread_server_name:从 SNI 扩展中提取的请求域名。
$ssl_preread_protocol:客户端支持的最高 TLS 版本。
$ssl_preread_alpn_protocols:客户端建议的 ALPN 协议列表。
重要提示:ssl_preread 与 ssl 指令(用于解密)不能同时使用于同一个 server 块。因为 ssl_preread 需要原始的加密数据流,而 ssl 指令会将其解密。
实践:基于 SNI 的加密流量路由
本示例演示如何使用 ssl_preread 模块,根据客户端请求的域名(SNI),将加密的 HTTPS 流量透明转发至不同的上游服务器。
步骤1:编译启用模块
./configure --prefix=/usr/local/nginx --with-stream --with-stream_ssl_preread_module
make -j $(nproc) && make install
步骤2:配置基于 SNI 的路由
worker_processes auto;
events {
worker_connections 1024;
}
stream {
log_format ssl_basic '$remote_addr ... ssl_preread=[$ssl_preread_server_name, $ssl_preread_protocol, $ssl_preread_alpn_protocols]';
access_log /var/log/nginx/stream_access.log ssl_basic;
# 根据预读的域名映射上游服务器
map $ssl_preread_server_name $proxy_server {
test.weiyigeek.top www.weiyigeek.top:443; # 路由到首页
blog.weiyigeek.top blog.weiyigeek.top:443; # 路由到博客
default 10.20.172.213:8443; # 默认后端
}
server {
listen 8443; # 注意这里没有`ssl`参数
set_real_ip_from 10.20.172.0/24;
resolver 8.8.8.8 valid=30s;
ssl_preread on; # 启用预读
proxy_pass $proxy_server; # 动态路由
}
}
步骤3:测试验证
配置 DNS 或 hosts 文件,将测试域名指向 Nginx 服务器 IP。
- 访问
https://test.weiyigeek.top:8443,流量应被转发至作者首页。

- 访问
https://blog.weiyigeek.top:8443,流量应被转发至作者博客。

- 直接访问 IP
https://10.20.172.214:8443(无 SNI),流量将转发至默认后端。
步骤4:查看预读日志
日志清晰记录了预读模块提取的信息,证明路由决策的依据。
tail -f /var/log/nginx/stream_access.log
# 输出示例:ssl_preread=[blog.weiyigeek.top, TLSv1.3, h2,http/1.1]
# 输出示例:ssl_preread=[test.weiyigeek.top, TLSv1.3, h2,http/1.1]
# 输出示例:ssl_preread=[, TLSv1.3, h2,http/1.1] # 无SNI情况

总结
通过本文的详解与实践,我们掌握了 Nginx Stream 模块中两个处理 SSL 的关键能力:
ngx_stream_ssl_module:用于在四层代理中终止 TLS/SSL 连接,将加密流量转换为明文 TCP 转发至上游,适用于需要解密审计或上游服务不支持 HTTPS 的场景。
ngx_stream_ssl_preread_module:用于在不中断加密的前提下,智能读取 TLS 握手信息(如 SNI、ALPN),并基于这些信息实现灵活的加密流量路由。这在需要透明转发 HTTPS 或其它基于 TLS 的协议(如 SSH over TLS)时非常有用,是构建现代、智能的网络四层网关的核心组件。
理解这两个模块的差异与适用场景,能帮助你在实际的基础架构工作中,更精准地设计流量治理方案。无论是用于安全隔离、协议转换还是智能路由,Nginx Stream 都提供了强大而灵活的工具集。

希望这篇深入浅出的指南能为你带来启发。如果你在实践过程中有任何心得或疑问,欢迎在云栈社区与其他开发者交流探讨。