Nginx Stream模块实现TCP透明转发透传源IP的唯一可行方案是启用proxy_protocol:一、listen后加proxy_protocol参数;二、配置set_real_ip_from指定可信代理网段;三、上游服务必须支持PROXY协议解析,否则连接异常或IP显示为Nginx本机地址。

Nginx 本身无法实现网络层意义上的“透明代理”——即不改写原始目的 IP、在内核层面截获并转发流量。所谓“TCP 透明转发 + 透传真实 IP”,实际是指在四层代理中尽可能保留客户端源 IP,并让后端服务能可靠识别它。这需要 stream 模块配合 proxy_protocol 协议,且依赖下游服务主动解析,不是开个开关就能自动生效。
用 proxy_protocol 传递真实客户端 IP
这是最常用也最可行的方式。Nginx 在 TCP 层无法直接把 $remote_addr 写进原始包头,但可以通过 PROXY 协议在连接建立初期,以明文方式在 TCP 数据流开头插入一段标识客户端地址的头部(如 PROXY TCP4 192.168.1.100 10.0.2.5 34292 443)。
- 必须在 Nginx 的 stream 块中启用:proxy_protocol on;
- 后端服务(如另一台 Nginx、HAProxy、或自研服务)必须支持并开启 PROXY 协议解析,否则会把这段头部当作非法数据丢弃或报错
- 如果后端是 Nginx,需配置:set_real_ip_from(指定可信代理地址)+ real_ip_header proxy_protocol,才能让 $remote_addr 正确取值
确保 Nginx 编译时启用了 stream 模块
很多发行版预装的 Nginx 默认不带 stream 支持。运行 nginx -V 2>&1 | grep -o with-stream 检查是否启用。
- 若无输出,需重新编译:configure 时加 --with-stream --with-stream_realip_module
- stream_realip_module 是解析 PROXY 协议所必需的,否则 set_real_ip_from 不生效
- 配置位置必须与 http{} 同级,不能嵌套在 http 块里
典型 TCP 透传配置示例
以下是一个监听 8080 端口、转发到内部 MySQL 实例,并透传客户端 IP 的 stream 配置:
stream {
upstream mysql_backend {
server 10.10.20.5:3306;
}
<pre class="brush:php;toolbar:false;">server {
listen 8080;
proxy_pass mysql_backend;
proxy_protocol on;
proxy_timeout 1h;
# 可选:记录日志便于排查
access_log /var/log/nginx/stream-access.log proxy;
}}
注意:proxy_protocol on 只负责发,不负责收;后端必须能收并解析,否则连接可能失败或 IP 显示为 Nginx 本机地址。
为什么不能靠 X-Forwarded-For?
X-Forwarded-For 是 HTTP 协议头,仅适用于七层(HTTP/HTTPS)代理。 TCP 代理没有请求头概念,所以该字段对 MySQL、Redis、SMTP 等纯 TCP 服务完全无效。强行在 stream 块里写 proxy_set_header 会报错或被忽略。
- HTTP 场景用 proxy_set_header X-Real-IP $remote_addr 和 X-Forwarded-For
- TCP 场景唯一标准方案就是 PROXY 协议,别无替代
- 若后端不支持 PROXY 协议,只能改服务代码或加一层兼容代理(如 HAProxy 做中间桥接)











