Nginx | stream 四层反向代理:SSL、PREREAD 阶段模块指令浅析与实践

知识是人生的灯塔,只有不断学习,才能照亮前行的道路

📢 大家好,我是 WeiyiGeek,一名深耕安全运维开发(SecOpsDev)领域的技术从业者,致力于探索DevOps与安全的融合(DevSecOps),自动化运维工具开发与实践,企业网络安全防护,欢迎各位道友一起学习交流、一起进步 🚀,若此文对你有帮助,一定记得点个关注⭐与小红星❤️或加入到作者知识星球『 全栈工程师修炼指南』,转发收藏学习不迷路 😋 。

SSL 阶段

描述:前文《Nginx | stream 四层反向代理:PREACCESS、ACCESS 阶段并发与访问限制模块浅析与实践》作者讲解了四层反向代理并发数限制以及与访问限制 ,由于互联网环境对安全性要求越来越高,要求中间件或应用在处理TCP协议时支持携带 TLS/SSL 的安全通信通讯,作为负载均衡的 Nginx 当然也是支持的,在四层反向代理中能够处理来自下游客户端带有 TLS/SSL 加密的连接,并透传裸TCP至上游服务器。

温馨提示:若文章代码块中存在乱码或不能复制,请联系作者,也可通过文末的阅读原文链接,加入知识星球中阅读,原文链接:https://articles.zsxq.com/id_t5z28zcqv7us.html

使用 TLS/SSL 的三种应用场景如下所示,其中安全与非安全通信标识,绿色线条表示使用TLS/SSL的安全网络通信(如HTTPS),蓝色线条表示未加密的裸TCP协议。

  • • 场景1:透传SSL流,Nginx 不解析SSL,直接将客户端发来的带有SSL协议的TCP流转发给上游服务器。

  • • 场景2:剥离SSL层(重点学习),Nginx 解析客户端发来的TLS协议,将其转换为裸TCP协议后再发送至上游服务。

  • • 场景3:后端加密,客户端与NGX之间无SSL协议,但NGX向上游服务发送请求时启用SSL/TLS。

weiyigeek.top-四层代理使用 `TLS/SSL` 的三种应用场景图

◆ ssl 模块

描述:ngx_stream_ssl_module 模块使stream反向代理支持对下游客户端的TLS/SSL协议处理,默认不编译进NGX,可通过 --with-stream_ssl_module 参数显式加入该模块。

指令参数

实际上 steam 中的 ssl 指令参数与 HTTP 模块中的 ssl 指令几乎完全相同,大家可参考 ngx_stream_ssl_module 模块文档了解,此处仅列出部分常用指令参数。

go 复制代码
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(Application-Layer Protocol Negotiation)协议,例如:h2 和 http/1.1
    ssl_alpn    h2 http/1.1;

# 在 OpenSSL 1.0.2 及更高版本中支持不同的证书和密钥对,例如:RSA and 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;

# 支持的协议版本列表,例如:TLSv1.2 和 TLSv1.3
    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握手超时时间,默认60秒。由于 http 模块中不关注握手超时,此处仅在 stream 模块中才有。
    ssl_handshake_timeout 10s;
  }
}

weiyigeek.top-stream ssl 常用指令图

  • ssl_alpn :支持的ALPN协议列表。
go 复制代码
Syntax: ssl_alpn protocol ...;
Default: ---
Context: stream, server

# 例如,表示支持 HTTP/2 和 HTTP/1.1 协议。
ssl_alpn    h2 http/1.1;
  • ssl_certificate: 指定服务器证书文件路径,用于SSL握手时验证服务端身份。
go 复制代码
Syntax: ssl_certificate file;
Default: ---
Context: stream, server
  • ssl_certificate_key: 指定服务器私钥文件路径,用于SSL握手时验证服务端身份。
go 复制代码
Syntax: ssl_certificate_key file;
Default:---
Context: stream, server
  • ssl_password_file: 指定包含私钥密码的文件路径,用于解密服务器私钥。例如,当使用加密的密钥文件时需要此参数。
go 复制代码
Syntax: ssl_password_file file;
Default: ---
Context: stream, server
  • ssl_ciphers: 指定支持的加密套件列表,用于协商安全连接。
go 复制代码
Syntax: ssl_ciphers ciphers;
Default: ssl_ciphers HIGH:!aNULL:!MD5;
Context: stream, server

# 例如:表示使用高强度加密算法且不包含匿名和MD5算法。
HIGH:!aNULL:!MD5
  • ssl_protocols: 指定支持的SSL协议版本列表,为了安全建议使用TLSv1.2 和 TLSv1.3 协议版本。
go 复制代码
Syntax: ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3];
Default: ssl_protocols TLSv1.2 TLSv1.3;
Context: stream, server

# 特别注意:
TLSv1.1 and TLSv1.2 : 仅在使用OpenSSL 1.0.1或更高版本时有效。
TLSv1.3 : 仅在使用OpenSSL 1.1.1或更高版本时有效,从 1.23.4 版本开始为缺省值。
  • ssl_handshake_timeout: 设置SSL握手超时时间,默认60秒,由于 http 模块中不关注握手超时,此处仅在 stream 模块中才有。
go 复制代码
Syntax: ssl_handshake_timeout time;
Default: ssl_handshake_timeout 60s;
Context: stream, server
  • ssl_certificate_cache:用于缓存服务器证书,减少每次SSL握手时的磁盘I/O开销。默认关闭,可通过设置参数启用。
go 复制代码
Syntax: ssl_certificate_cache off;
ssl_certificate_cache max=N [inactive=time] [valid=time];
Default: ssl_certificate_cache off;
Context: stream, server

# 参数说明:
max:设置缓存中可存储的客户端证书数量。
inactive: 设置缓存项在不被访问后多久自动失效,例如 60s。
valid:设置缓存项的有效期,例如 10m 表示有效期为 10 分钟。

# 例如,表示最多存储 1000 个证书,如果超过60秒未被访问则自动失效,有效期为 10 分钟。
ssl_certificate_cache max=1000 inactive=60s valid=10m;
  • ssl_session_cache: 设置SSL会话缓存,用于存储和复用之前的TLS握手信息。默认关闭,可通过设置参数启用。
go 复制代码
Syntax: ssl_session_cache off | none | [builtin[:size]] [shared:name:size];
Default: ssl_session_cache none;
Context: stream, server

# 参数说明:
off :禁用会话缓存。
none :测试模式,不实际缓存会话。
builtin[:size] :使用内置缓存,可选参数 size 指定最大内存大小,若如果没有指定大小,则等于20480个会话,另外使用内置缓存可能会导致内存碎片,所以通常不建议使用。
shared:name:size :使用共享区域缓存,其中 name 是缓存的名称,size 指定最大内存大小,1兆字节可以存储大约4000个会话。
  • ssl_session_tickets: 启用或禁用TLS会话票证(Session Tickets)功能,默认开启。
go 复制代码
Syntax: ssl_session_tickets on | off;
Default: ssl_session_tickets on;
Context: stream, server
  • ssl_session_ticket_key: 设置会话票据密钥,用于启用TLS会话票证(Session Tickets)功能。默认关闭,可通过设置参数启用,如果同一密钥必须在多个服务器之间共享,则该指令是必需的。
go 复制代码
Syntax: ssl_session_ticket_key file;
Default: ---
Context: stream, server

# 根据文件大小,可以使用AES256(用于80字节密钥,1.11.8)或AES128(用于48字节密钥)进行加密。
openssl rand 80 > ticket.key
ssl_session_ticket_key current.key;
  • ssl_session_timeout: 设置SSL会话超时时间,默认5分钟。
go 复制代码
Syntax: ssl_session_timeout time;
Default: ssl_session_timeout 5m;
Context: stream, server
  • ssl_verify_client: 启用或禁用客户端证书验证,默认关闭。
go 复制代码
Syntax: ssl_verify_client on | off | optional | optional_no_ca;
Default: ssl_verify_client off;
Context: stream, server
  • ssl_verify_depth: 设置客户端证书链的验证深度,默认1, 从信任的根 CA 开始计数,不包含服务器证书本身,例如设置为2,则验证客户端证书链的前两个证书,但是值越大,验证可能越耗时
go 复制代码
Syntax: ssl_verify_depth number;
Default: ssl_verify_depth 1;
Context: stream, server
  • ssl_trusted_certificate: 指定一个或多个可信CA证书文件,用于验证客户端证书或OCSP响应。默认不启用此功能。
go 复制代码
Syntax: ssl_trusted_certificate file;
Default:---
Context: stream, server

另外,Stream SSL 模块中还提供了一些特殊的变量,大致分为如下几类,例如

安全套件信息:

  • $ssl_cipher:获取本次通信使用的确切安全套件

  • $ssl_client_ciphers:列出客户端(如浏览器)支持的所有安全套件

  • $ssl_protocol:显示当前使用的TLS协议版本(如 TLSv1.2)

  • $ssl_alpn_protocol:显示客户端支持的ALPN协议,例如 h2 表示HTTP/2。

  • $ssl_curves:显示协商使用的椭圆曲线(如 X25519, secp384r1)

  • $ssl_ech_status: 显示客户端支持的加密扩展协议状态,例如 "FAILED", "BACKEND", "GREASE", "SUCCESS", or "NOT_TRIED" (1.29.4);

证书内容信息:

  • $ssl_client_raw_cert:返回原始客户端证书内容。

  • $ssl_client_escaped_cert:返回经URL编码后的客户端证书。

  • $ssl_client_cert:返回客户端证书的PEM格式内容。

  • $ssl_client_fingerprint:返回客户端证书的SHA-1指纹。

证书结构化信息:特别注意 stream 模块不支持旧版(legacy)以反斜杠分隔的DN格式,仅支持标准格式。

  • $ssl_server_name:通过SNI扩展获取客户端请求的域名。

  • $ssl_client_i_dn:获取客户端证书的Issuer DN信息(如国家、组织等)。

  • $ssl_client_s_dn: 获取客户端证书的Subject DN信息,每行前加Tab符,便于打印输出。

证书有效期信息:

  • $ssl_client_v_end:返回客户端证书的过期时间(格式示例:May 17 09:35:15 2033 GMT)。

  • $ssl_client_v_remain:返回客户端证书剩余有效天数(单位:天),例如值为 3649 表示约十年。

  • $ssl_client_v_start:返回客户端证书由CA颁发的起始日期。

连接有效性相关变量:

  • $ssl_client_serial_no:返回客户端证书的序列号(十六进制格式)。

  • $ssl_client_verify:返回客户端证书验证结果:失败时显示具体原因,未验证返回空字符串,成功返回 "SUCCESS"。

  • $ssl_session_id:获取已建立连接的SSL会话ID,在OpenResty中常用。

  • $ssl_session_reused:指示会话是否被复用,是则返回 "r",否则为 "."。

实践示例

目的:展示 Nginx 作为四层反向代理时将客户端带SSL的TCP流剥离SSL层后,以裸TCP形式转发至上游服务器。特别注意,虽使用HTTP协议进行演示,但 Nginx 的stream模块仅处理TCP与SSL层,不关心应用层协议。

步骤 01.重新构建编译 Nginx 以启用 stream ssl 模块。

go 复制代码
# 构建
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --sbin-path=/usr/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --pid-path=/usr/local/nginx/nginx.pid --error-log-path=/var/log/nginx/logs/error.log --http-log-path=/var/log/nginx/logs/access.log --lock-path=/var/run/nginx.lock --modules-path=/usr/local/nginx/modules --with-http_stub_status_module --with-http_realip_module --with-http_v2_module --with-http_ssl_module --with-http_slice_module --with-stream --with-stream_realip_module  --with-stream_ssl_module --with-cc-opt=-O2 --with-compat

# 编译安装
make -j $(nproc) && make install

步骤 02.准备两台 Nginx 服务器,一台作为 stream 四层转发服务(192.168.10.2),另一台作为上游服务器(192.168.10.3)提供 http 服务。

stream 四层转发服务器配置示例:

go 复制代码
tee /usr/local/nginx/conf/nginx.conf <<'EOF'
worker_processes auto;
events {
  worker_connections  1024;
}

# stream 模块配置块
stream {
# 定义一个日志模式格式,加入了 $ssl_session_reused变量,以便记录会话是否被复用。
  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';

# 开启日志文件描述符缓存,减少打开和关闭文件的开销
  open_log_file_cache max=1000 inactive=60s min_uses=2 valid=1m;

# 开启访问日志记录,注意这里为了测试未启用缓存,生产环境中推荐使用缓存。
  error_log /var/log/nginx/stream_error.log debug;
  access_log /var/log/nginx/stream_access.log basic;

# 定义一个四层TCP服务
  server {
# 监听 8443 端口,启用 ssl
    listen 8443 ssl; 

# 设置可信客户端IP地址,即真实地址将排除下述IP或IP段的地址。
    set_real_ip_from 127.0.0.1;
    set_real_ip_from 10.20.172.0/24;

# SSL 证书文件:server.weiyigeek.top
    ssl_certificate /usr/local/nginx/certs/server.crt;
    ssl_certificate_key /usr/local/nginx/certs/server.key;

# 支持的 SSL/TLS 协议版本
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;

# 支持的 SSL/TLS 加密套件
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-DH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES;

# SSL 会话缓存
    ssl_session_cache shared:SSL:10m;
# SSL 会话超时时间
    ssl_session_timeout 10m;
# 优先使用服务器端支持的加密套件
    ssl_prefer_server_ciphers on;
# SSL 会话票据复用
    ssl_session_tickets on;

# 转发至上游服务器,此处仅为示例,实际应用中应替换为实际服务地址和端口。
    proxy_pass 10.20.172.213:8011;
  }
}
EOF

上游服务器(192.168.10.3)提供 http 服务配置示例:

go 复制代码
$ echo'https://www.weiyigeek.top' > /usr/local/nginx/html/index.html
$ vim /usr/local/nginx/conf.d/upstream.conf

# 模拟上游服务器其监听端口为8011
server {
  listen 8011;
  server_name _;
  charset utf-8;
  default_type text/plain;

# 静态文件服务配置
  location / {
    root  /usr/local/nginx/html;
    index index.html;
  }

# 返回当前时间戳作为响应内容
  location ^~ /api {
return 200 '$time_iso8601 $request_id $server_addr:$server_port $request_uri\n';
  }
}

步骤 03.启动 Nginx 服务并使用浏览器访问测试。

go 复制代码
$ nginx -t && nginx -s reload
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successfu

# 硬解析设置或添加到内部DNS解析记录,以便浏览器能正确访问。
$ cat /etc/hosts
192.168.10.2 server.weiyigeek.top

在上游服务中使用 tcpdump 抓取 ens18 网卡的 8011 通讯包,命令 tcpdump -i ens18 port 8011 -A -s 0,然后在浏览器访问 https://server.weiyigeek.top:8443/index.html 以及 https://server.weiyigeek.top:8443/api 并查看 tcpdump 抓包结果。
weiyigeek.top-抓包验证 steam 四层转发图

步骤 04.另外,从我们记录的 stream_access.log 日志中可查看到客户端与服务端之间的SSL会话是否复用等信息。

go 复制代码
$ tail -f /var/log/nginx/stream_access.log
10.20.172.103:61065 "10.20.172.103:61065" [27/Jan/2026:11:52:41 +0800] TCP 200 0 0 60.004 "-" TLSv1.3 .   # 此处显示 . 表示会话未被复用,即客户端与服务端之前的连接是全新的。
10.20.172.103:61064 "10.20.172.103:61064" [27/Jan/2026:11:53:18 +0800] TCP 200 1273 2221 96.621 "-" TLSv1.3 . 
10.20.172.103:61501 "10.20.172.103:61501" [27/Jan/2026:11:59:20 +0800] TCP 200 0 0 60.001 "-" TLSv1.3 r   # 此处显示 r 表示会话被复用,即客户端与服务端之前的连接是复用的。
10.20.172.103:61499 "10.20.172.103:61499" [27/Jan/2026:11:59:25 +0800] TCP 200 253 783 65.002 "-" TLSv1.3 r # 此处显示 r 表示会话被复用,即客户端与服务端之前的连接是复用的。

$ tail -f /var/log/nginx/stream_error.log
2026/01/27 11:51:41 [info] 3093559#0: *35 proxy 10.20.172.214:41324 connected to 10.20.172.213:8011
2026/01/27 11:51:41 [info] 3093560#0: *36 proxy 10.20.172.214:41332 connected to 10.20.172.213:8011
2026/01/27 11:52:41 [info] 3093560#0: *36 upstream disconnected, bytes from/to client:0/0, bytes from/to upstream:0/0
2026/01/27 11:53:18 [info] 3093559#0: *35 upstream disconnected, bytes from/to client:2221/1273, bytes from/to upstream:1273/2221

weiyigeek.top-请求结果与请求日志查看图

至此,本小节实践了如何解析、验证客户端 SSL 证书,并将带有 SSL 的TCP 流剥离 SSL 层,并以裸 TCP 方式转发至上游服务器,此模式在实际场景中非常常见,尤其适用于非HTTP类协议的四层代理场景,应用范围广泛。


PREREAD 阶段

描述:上文讲解了如何在 SSL 阶段剥离 SSL 层,并以裸 TCP 方式转发至上游服务器,这虽然是最常用的场景,但是在某些场景下,例如上游和下游均使用 SSL 协议时,Nginx 需在不剥离 SSL 层的情况下,解析客户端Hello消息中的TLS版本、SNI域名和ALPN协议等信息。若直接转发带 SSL 的 TCP 流,则无法获取握手信息,若剥离 SSL 则改变了传输协议,无法满足透明转发需求,在这样的背景问题下,Nginx 提供了 stream SSL preread 模块来解决。
weiyigeek.top-SSL preread 模块使用场景图

◆ ssl_preread 模块

描述:stream_ssl_preread_module 模块默认未编译进 Nginx,需通过 --with-stream_ssl_preread_module 显式启用,在不剥离SSL的情况下解析的下游发来的SSL握手消息(Client Hello)消息中的TLS版本、SNI域名和ALPN协议等信息,并利用这些变量实现基于四层反向代理的上游服务动态选择,提升负载均衡灵活性,例如:

go 复制代码
# 根据服务器名称选择上游
map $ssl_preread_server_name$name {
  backend.example.com      backend1;
  default                  backend2;
}

# 根据协议选择上游
map $ssl_preread_alpn_protocols$proxy {
  ~\bh2\b           127.0.0.1:8001;
  ~\bhttp/1.1\b     127.0.0.1:8002;
  ~\bxmpp-client\b  127.0.0.1:8003;
}

# 根据SSL协议版本选择上游
map $ssl_preread_protocol$upstream {
""        ssh.example.com:22;
"TLSv1.2" new.example.com:443;
  default   tls.example.com:443;
}

# 例如 ssh 与 https 在同一端口,根据协议分流
server {
  listen      192.168.0.1:443;
  proxy_pass  $upstream;
  ssl_preread on;
}

特别注意:使用 stream_ssl_preread 模块时,向上游发送的是完整的SSL/TLS加密流,不可同时启用 stream_ssl 模块,若已执行SSL解密(进入明文TCP流),则 ssl_preread 阶段无法再获取原始 Client Hello 信息,并会导致冲突。

指令参数

  • ssl_preread :是否允许在预读阶段从ClientHello消息中提取信息。
go 复制代码
Syntax: ssl_preread on | off;
Default: ssl_preread off;
Context: stream, server
  • preread_buffer_size : 设置用于解析SSL握手数据的缓冲区大小,默认为 16KB,属于 stream_core_module 的一部分,因此可以在 stream 或 server 级别设置。
go 复制代码
Syntax: preread_buffer_size size;
Default: preread_buffer_size 16k;
Context: stream, server
  • preread_timeout: 设置等待 Client Hello 完成的时间,默认 30 秒,属于 stream_core_module 的一部分,因此可以在 stream 或 server 级别设置。
go 复制代码
Syntax: preread_timeout timeout;
Default: preread_timeout 30s;
Context: stream, server

另外,该模块提供了以下变量,以供后续模块选择使用:

  • $ssl_preread_protocol: 客户端支持的最高TLS版本(如 TLSv1.2, TLSv1.3),可用于根据安全等级选择不同上游服务器。

  • $ssl_preread_server_name: 从 SNI(Server Name Indication)扩展中提取的服务器域名,用于基于域名的路由决策。

  • $ssl_preread_alpn_protocols: 客户端建议使用的应用层协议(如 h2 表示 HTTP/2,http/1.1 等),可用于协议优化匹配。

示例演示

目的:使用 stream_ssl_preread 模块实现根据 $ssl_preread_server_name 获取客户端请求的域名,并反向代理此域名对应的上游服务器。

步骤 01.启用 stream_ssl_preread 模块加入到 Nginx 编译配置中,并重新编译安装。

go 复制代码
# 构建
./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --sbin-path=/usr/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --pid-path=/usr/local/nginx/nginx.pid --error-log-path=/var/log/nginx/logs/error.log --http-log-path=/var/log/nginx/logs/access.log --lock-path=/var/run/nginx.lock --modules-path=/usr/local/nginx/modules --with-http_stub_status_module --with-http_realip_module --with-http_v2_module --with-http_ssl_module --with-http_slice_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt=-O2 --with-compat

# 编译安装
make -j $(nproc) && make install

步骤 02.配置 Nginx 以使用 stream_ssl_preread 模块提取的Client Hello 消息中关键变量实现基于域名的反向代理。

go 复制代码
tee /usr/local/nginx/conf/nginx.conf <<'EOF'
worker_processes auto;
events {
  worker_connections  1024;
}
# stream 模块配置块
stream {
# 定义一个日志模式格式。
  log_format ssl_basic '$remote_addr:$remote_port "$realip_remote_addr:$realip_remote_port" [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time ssl_preread=[$ssl_preread_server_name, $ssl_preread_protocol, $ssl_preread_alpn_protocols] '
'$ssl_client_i_dn $ssl_client_s_dn $ssl_client_v_end $ssl_curves';

# 开启日志文件描述符缓存,减少打开和关闭文件的开销
# open_log_file_cache max=1000 inactive=60s min_uses=2 valid=1m;

# 开启访问日志记录,注意这里为了测试未启用缓存,生产环境中推荐使用缓存。
  error_log /var/log/nginx/stream_error.log debug;
  access_log /var/log/nginx/stream_access.log ssl_basic;

# 反向代理到上游服务器,此处使用 map 根据域名选择不同的后端服务。
  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;     # 其他情况默认指向默认服务
  }

# 定义一个四层TCP服务
  server {
# 监听 8443 端口
    listen 8443; 

# 设置可信客户端IP地址,即真实地址将排除下述IP或IP段的地址。
    set_real_ip_from 127.0.0.1;
    set_real_ip_from 10.20.172.0/24;

# 解析服务域名地址
    resolver 61.128.128.68 valid=30s;
    resolver_timeout 60s;

# 开启SSL预读阶段,以便提取客户端支持的TLS版本、SNI域名和ALPN协议等信息。
    ssl_preread on;

# 根据客户端请求的域名动态选择上游服务器
    proxy_pass $proxy_server;
  }
}
EOF

步骤 03.验证配置并重新启动 Nginx 服务,将测试域名使用硬解析方式指向 nginx 服务器IP地址 10.20.172.214 (根据实际情况更改) ,并使用浏览器分别访问 test.weiyigeek.top:8443blog.weiyigeek.top:8443 以及 10.20.172.214:8443 测试配置是否生效。

go 复制代码
nginx -t && nginx -s reload

验证1:访问 https://test.weiyigeek.top:8443 ,正常情况下应能正常访问作者的首页,可在开发者工具【网络】选项卡,查看到未发生HTTP重定向,证明全程处于四层转发模式。
weiyigeek.top-验证1访问作者首页图

验证2:访问 https://blog.weiyigeek.top:8443 ,正常情况下应能正常访问作者的博客。
weiyigeek.top-验证2访问博客图

验证3:访问 https://10.20.172.214:8443 ,此时 $ssl_preread_server_name 变量将为空,因此正常情况下应能正常访问 10.20.172.213:8443 这个内部服务。
weiyigeek.top-验证3访问内部服务图

步骤 04.查看 Nginx 日志文件,验证是否正确记录了 $ssl_preread_server_name 以及协议版本,ALPN协议等信息。

go 复制代码
tail -f /var/log/nginx/stream_access.log
10.20.172.103:52166 "10.20.172.103:52166" [27/Jan/2026:16:07:26 +0800] TCP 200 1423439 6485 223.494 ssl_preread=[blog.weiyigeek.top, TLSv1.3, h2,http/1.1] - - - -  # 验证2
10.20.172.103:52302 "10.20.172.103:52302" [27/Jan/2026:16:07:53 +0800] TCP 200 3405 1900 0.016 ssl_preread=[, TLSv1.3, h2,http/1.1] - - - -                         # 验证3
10.20.172.103:52286 "10.20.172.103:52286" [27/Jan/2026:16:08:37 +0800] TCP 200 221095 3515 67.570 ssl_preread=[test.weiyigeek.top, TLSv1.3, h2,http/1.1] - - - -    # 验证1

weiyigeek.top-利用日志文件验证ssl_preread模块图

至此,作者带领大家一起利用 stream_ssl_preread 模块实现了在保持SSL加密流完整性的前提下,提取 Client Hello 中 $ssl_preread_protocol$ssl_preread_server_name$ssl_preread_alpn_protocols 三个关键变量,并利用 map 模块实现了基于 $ssl_preread_server_name 变量动态选择上游服务。在实际的生产场景中非常场景,尤其适用于非HTTP类协议的四层代理场景,基于TLS版本、域名或应用层协议的精细化上游服务选择,增强了四层反向代理的灵活性与智能化水平。

END

加入:作者【全栈工程师修炼指南】知识星球

『 全栈工程师修炼指南』星球,主要涉及全栈工程师(Full Stack Development)实践文章,持续更新包括但不限于企业SecDevOps和网络安全等保合规、安全渗透测试、编程开发、云原生(Cloud Native)、物联网工业控制(IOT)、人工智能Ai,从业书籍笔记,人生职场认识等方面资料或文章。

Q: 加入作者【全栈工程师修炼指南】星球后有啥好处?

✅ 将获得作者最新工作学习实践文章以及网盘资源。

✅ 将获得作者珍藏多年的全栈学习笔记(需连续两年及以上老星球友,也可单次购买)

✅ 将获得作者专门答疑学习交流群,解决在工作学习中的问题。

✅ 将获得作者远程支持(在作者能力范围内且合规)。

获取:作者工作学习全栈笔记

作者整理了10年的工作学习笔记(涉及网络、安全、运维、开发),需要学习实践笔记的看友,可添加作者微信或者回复【工作学习实践笔记】,当前价格¥299,除了获得从业笔记的同时还可进行问题答疑以及每月远程技术支持,希望大家多多支持,收获定大于付出!

知识推荐 往期文章

若文章对你有帮助,请将它转发给更多的看友,若有疑问的小伙伴,可在评论区留言你想法哟 💬!

相关推荐
极新2 小时前
智启新篇,智创未来,“2026智造新IP:AI驱动品牌增长新周期”峰会暨北京电子商务协会第五届第三次会员代表大会成功举办
人工智能·网络协议·tcp/ip
M158227690552 小时前
TCP转LORA产品说明及应用案例
网络·网络协议·tcp/ip
旖旎夜光2 小时前
Linux(13)(中)
linux·网络
来可电子CAN青年2 小时前
CAN总线远距离传输老断网?Fx灯不闪别慌,这几招让你的通信“稳如泰山”!
网络
独行soc2 小时前
2026年渗透测试面试题总结-18(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
云小逸2 小时前
【nmap源码解析】Nmap OS识别核心模块深度解析:osscan2.cc源码剖析(1)
开发语言·网络·学习·nmap
自不量力的A同学2 小时前
Solon AI v3.9 正式发布:全能 Skill 爆发
java·网络·人工智能
威迪斯特3 小时前
CentOS图形化操作界面:理论解析与实践指南
linux·运维·centos·组件·图形化·桌面·xserver
一方热衷.3 小时前
在线安装对应版本NVIDIA驱动
linux·运维·服务器