workerman 不自动解析 x-real-ip/x-forwarded-for,必须 nginx 透传 + 应用层通过 $request->header('x-real-ip') 安全读取;cli 模式下 $_server 无法获取请求头,$connection->remoteaddress 仅为 tcp 层地址,非业务真实 ip。

直接看结论:Workerman 本身不自动解析 X-Real-IP 或 X-Forwarded-For,必须靠代理(Nginx/CDN)透传 + 应用层手动读取,且不同协议(HTTP/WS/WSS)读取方式不同。漏掉任一环,$connection->remoteAddress 或 $_SERVER['REMOTE_ADDR'] 拿到的永远是上一跳代理 IP。
WebSocket 连接中从 $request 读取 X-Real-IP
这是最常见也最容易出错的场景。Nginx 反向代理 WebSocket 时,必须在 location 块里透传头,Workerman v5 要在 onWebSocketConnect 回调中从 $request 对象取值:
- Nginx 配置必须包含:
proxy_set_header X-Real-IP $remote_addr;(首层代理)或proxy_set_header X-Real-IP $proxy_add_x_forwarded_for;(多级追加) - Workerman v5 中不能用
$_SERVER['HTTP_X_REAL_IP']—— 因为 CLI 模式下$_SERVER不含 HTTP 头,而$request是解析后的对象 - 正确写法:
$connection->realIP = $request->header('x-real-ip');(注意 key 是小写) - 如果 Nginx 没透传或传了空值,
$request->header()返回null,需做空判断
HTTP 协议下用 $_SERVER 读取失败的典型原因
很多人在 HTTP Worker 里写 $_SERVER['HTTP_X_REAL_IP'] 却拿不到值,本质是混淆了 SAPI 环境:
- Workerman 的 HTTP Worker 是纯 CLI 进程,不走 PHP-FPM 或 Apache,
$_SERVER不会自动注入 HTTP 请求头 -
$_SERVER['HTTP_*']只在 CGI/FastCGI 环境下由 Web 服务器填充;Workerman 中它只保留启动时的环境变量,和请求无关 - HTTP 协议下,真实请求头必须通过
$request->header()获取,和 WebSocket 一致 - 如果你用的是 GatewayWorker 架构,BusinessWorker 收到的请求是 GatewayWorker 转发的私有协议包,
$_SERVER更不可信,必须依赖 GatewayWorker 在转发时注入的字段(如$connection->client_ip)
CDN 或多级代理下只取第一个非可信 IP 的风险
当用户经过 CDN → Nginx → Workerman 三层时,X-Forwarded-For 可能是 1.2.3.4, 5.6.7.8, 9.10.11.12(从左到右是原始客户端、CDN、Nginx)。但直接取 explode(',', $xff)[0] 极其危险:
- CDN 可能伪造
X-Forwarded-For,任意客户端也能在请求头里加这个字段 - 真正安全的做法是:只信任你控制的代理网段(如 Nginx 内网 IP),然后从右往左剔除所有可信 IP,第一个剩下的才是真实客户端 IP
- Workerman 没内置该逻辑,需自行实现类似
getRealIpFromXff($xff, ['192.168.10.0/24', '127.0.0.1'])的函数 - 若你用的是 Webman(非 Workerman),它已封装
Request::getRealIp(),但前提是 Nginx 启用了realip模块并配好set_real_ip_from
最易被忽略的一点:Workerman 的 $connection->remoteAddress 是 TCP 层原始地址,永远反映“谁连了我”,而不是“谁发起请求”。想还原业务意义上的真实 IP,必须放弃对这个字段的依赖,全程走 HTTP 头透传与校验链路。











