Nginx TCP代理(stream模块)无法基于HTTP Host头路由,但可通过ssl_preread提取SNI实现四层域名级转发;需启用--with-stream_ssl_preread_module,配置map匹配$ssl_preread_server_name,仅支持TLS流量且不解密。

Nginx 的 TCP 代理(即 stream 模块)本身不解析 HTTP 协议,因此无法直接读取 Host 请求头或 URL 路径。它工作在四层(传输层),只能基于 IP、端口、TLS 握手阶段的 SNI(Server Name Indication)信息做路由决策。所以严格来说,TCP 代理不能实现“基于域名”的路由分发,但可以基于 SNI 实现近似效果——前提是客户端使用 HTTPS/TLS 连接,且发送了 SNI 扩展。
✅ 基于 SNI 的 TCP 代理路由(实际可行方案)
SNI 是 TLS 握手时客户端主动发送的一个字段,用于告知服务器它想访问哪个域名(如 api.example.com)。Nginx stream 模块支持提取并匹配该字段,从而将连接转发到不同后端。
你需要:
- 启用
stream模块(编译时需含--with-stream --with-stream_ssl_preread_module) - 客户端必须走 TLS(如 HTTPS、TLS 封装的 MySQL/PostgreSQL/Redis 等)
- Nginx 配置中启用
ssl_preread on
示例配置(/etc/nginx/conf.d/stream-sni.conf):
stream {
# 提取 SNI 并映射到后端服务
map $ssl_preread_server_name $backend {
hostnames;
api.example.com 192.168.1.10:443;
admin.example.com 192.168.1.11:8443;
*.example.com 192.168.1.12:443; # 泛匹配(需 DNS 已泛解析)
default 127.0.0.1:8080; # 默认兜底
}
upstream api_cluster {
server 192.168.1.10:443;
}
upstream admin_cluster {
server 192.168.1.11:8443;
}
server {
listen 443;
ssl_preread on; # 关键:开启 SNI 预读
proxy_pass $backend;
proxy_timeout 1s;
proxy_responses 1;
}
}⚠️ 注意:
$ssl_preread_server_name只在 TLS 握手初期可用,不依赖证书验证,也不解密流量。它仅用于路由,后续加密流直接透传。
❌ 为什么不能靠 HTTP Host 头做 TCP 层路由?
-
stream模块不处理应用层(七层)数据,看不到 HTTP 的Host、Path或任何明文请求内容。 - 如果你把纯 HTTP 流量(非 TLS)交给
stream处理,它只能按目标端口或客户端 IP 分发,无法识别域名。 - 想靠域名分流 HTTP 流量?必须用
http模块 +server_name+proxy_pass(即七层反向代理)。
? 对比:HTTP 域名路由 vs TCP+SNI 路由
| 维度 | HTTP 模块(七层) | Stream 模块(四层 + SNI) |
|---|---|---|
| 支持协议 | HTTP/HTTPS(需终止 TLS) | HTTPS、TLS 封装的任意协议(MySQL、SMTP、自定义 TCP 服务) |
| 域名识别依据 |
Host 请求头(明文) |
TLS 握手中的 SNI 字段(未解密) |
| 是否需要证书 | HTTPS 终止时需要 | 不需要(仅读 SNI,不验签不解密) |
| 性能开销 | 较高(解密、解析 HTTP) | 极低(仅解析握手包前 few bytes) |
| 典型用途 | Web 站点多域名托管、API 网关 | TLS 加密的数据库代理、邮件网关、私有协议网关 |
? 补充建议
- 确保客户端支持并发送 SNI(现代浏览器、curl ≥7.18.1、OpenSSL ≥1.0.0 默认开启)。
- 若后端是 HTTPS 服务,Nginx
stream仅做透传,证书由后端自己提供;若希望 Nginx 统一卸载 TLS,就不能用 stream,得切回 http 模块。 - 泛域名匹配(如
*.example.com)在map中用hostnames指令支持,但注意:$ssl_preread_server_name是完整字符串,*.example.com匹配的是字面量,不是通配符规则——实际生效靠map的hostnames模式自动做前缀/后缀匹配。
不复杂但容易忽略:SNI 路由本质是“TLS 层的域名提示”,不是真正的 HTTP 域名路由。选对模块,才能让流量走对路。



















