nginx 的 stream_ssl_preread 模块支持仅解析 clienthello 中的 sni 实现四层域名分流,无需终止 tls 或接触私钥;需编译启用、配置 stream 块、使用 ssl_preread on 和 map $ssl_preread_server_name,并设 default 处理无 sni 流量。

确认模块已启用
该模块不是默认开启的,需确保 Nginx 编译时包含 --with-stream_ssl_preread-module(同时依赖 --with-stream)。验证方式:
- 运行
nginx -V 2>&1 | grep stream_ssl_preread,有输出即表示已启用 - 若无输出,Ubuntu 20.04+ 可安装
nginx-full;CentOS/RHEL 需重新编译或换用支持该模块的发行版包
配置 stream 块解析 SNI
所有配置必须放在顶层 stream { ... } 块中,不能嵌套在 http 块内。核心是启用预读 + 映射变量:
-
ssl_preread on;必须写在server块内,且监听端口为 TLS 端口(如 443) - 用
map $ssl_preread_server_name $backend将域名映射为 upstream 名或 IP:port -
hostnames指令支持通配符(如*.example.com),增强匹配灵活性 -
default条目必不可少——应对 SNI 为空(旧客户端、非 TLS 流量或 TLS 1.2 未发送 SNI)
定义后端与代理逻辑
推荐使用命名 upstream,便于复用和管理:
- 每个 upstream 可配置多个 server,支持
backup、max_conns、weight等参数 -
proxy_pass $backend;中的$backend应指向 upstream 名(如backend_a),而非直接 IP - 添加
proxy_timeout 1s;和proxy_responses 1;可快速失败,避免因握手异常导致连接长时间阻塞
注意兼容性与安全边界
SNI 分流本质依赖客户端行为,实际部署需留有余地:
- TLS 1.2 允许客户端省略 SNI,部分 IoT 设备或老旧库可能不发;TLS 1.3 强制 SNI,但并非所有客户端都已升级
- 若需更高可靠性,可搭配 ALPN 判断协议类型(如
$ssl_preread_alpn_protocols匹配h2或http/1.1)作辅助路由 - 不建议在该层做证书校验(如
proxy_ssl_verify),那是http层或专用 TLS 终止设备的职责;stream 层只做透传











