You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Nginx实现动态TCP正向代理?解决PHP MySQL请求IP隐藏需求

能否用Nginx实现动态TCP代理?

首先明确:Nginx并非只能实现静态TCP隧道,纯核心的ngx_stream_core_module确实不支持解析URL参数(毕竟它是面向原始TCP流的,不处理HTTP协议层内容),但通过扩展或巧劲,我们完全可以实现你要的动态代理需求。

结合你隐藏PHP MySQL请求IP的场景,我整理了几种可行方案:

方案1:用OpenResty的Lua扩展实现动态TCP代理

如果你能使用OpenResty(Nginx+Lua生态),这是最灵活的方案。Lua脚本可以在TCP连接建立阶段,解析客户端发送的初始数据(比如自定义握手信息),动态指定转发目标。

比如针对你的MySQL场景,可以让PHP先发送一行包含目标信息的自定义前缀,再发送原生MySQL协议数据:

stream {
    server {
        listen 8080;
        content_by_lua_block {
            -- 读取客户端发送的第一行自定义目标信息
            local line, err = ngx.socket.tcp():receiveuntil("\r\n")()
            if not line then
                ngx.log(ngx.ERR, "Failed to read destination: ", err)
                return ngx.exit(444)
            end

            -- 解析格式:DEST: example.com:3306
            local dest = ngx.re.match(line, "^DEST: (.+)$")[1]
            if not dest then
                ngx.log(ngx.ERR, "Invalid destination format")
                return ngx.exit(444)
            end

            -- 建立到目标的连接并转发流量
            local sock = ngx.socket.tcp()
            local ok, err = sock:connect(dest)
            if not ok then
                ngx.log(ngx.ERR, "Failed to connect to ", dest, ": ", err)
                return ngx.exit(444)
            end

            -- 双向转发数据
            ngx.thread.spawn(function()
                while true do
                    local data, err = sock:receive(8192)
                    if not data then break end
                    ngx.print(data)
                    ngx.flush()
                end
            end)

            while true do
                local data, err = ngx.req.socket():receive(8192)
                if not data then break end
                sock:send(data)
            end
        }
    }
}

对应的PHP代码需要先发送目标信息,再建立MySQL连接:

// 先连接代理并发送目标
$proxySock = fsockopen('proxy_host', 8080);
fwrite($proxySock, "DEST: example.com:3306\r\n");

// 将socket传递给mysqli(PHP 7.1+支持)
$mysqli = mysqli_init();
$mysqli->set_opt(MYSQLI_OPT_SOCKET, $proxySock);
$mysqli->real_connect('', 'username', 'password', 'dbname');

方案2:静态批量配置(适合目标数量有限的场景)

如果你的目标MySQL服务器数量不多,完全可以为每个目标单独配置一个监听端口,简单直接:

stream {
    server {
        listen 8081;
        proxy_pass db1.example.com:3306;
    }
    server {
        listen 8082;
        proxy_pass db2.example.com:3306;
    }
    # 更多目标...
}

PHP里只需根据需求连接对应端口:mysqli_connect("proxy_host:8081", $user, $pass, $db);。这个方案无需额外扩展,但灵活性差,目标多了维护成本高。

方案3:使用第三方动态上游模块

如果你不想用Lua,可以尝试第三方模块ngx_stream_upstream_dynamic_module,它允许你通过HTTP接口动态添加/修改上游目标,无需重启Nginx。不过需要你自行编译Nginx并添加该模块,适合需要频繁变更目标的场景。

总结

回到你的问题:Nginx绝对可以实现动态TCP代理,只是核心stream模块做不到,需要借助OpenResty Lua或第三方模块。结合你的PHP MySQL场景,方案1(Lua+自定义握手)是最贴合动态需求的,方案2则适合简单场景。

内容的提问来源于stack exchange,提问作者Viktor Joras

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:53:18