websocket获取客户端真实ip需从tcp连接或http握手提取,代理/cdn会掩盖真实地址;须透传x-forwarded-for并校验私有网段,node.js用req.socket.remoteaddress+xff,spring需handshakeinterceptor注入request,libwebsockets需手动解析头,php依赖$_server对应字段,所有场景均需脱敏且强依赖代理配置。

WebSocket服务端无法直接从协议帧里拿到客户端IP,必须从底层TCP连接或HTTP握手请求中提取——而且代理、CDN、负载均衡器会掩盖真实地址,不处理 X-Forwarded-For 或 X-Real-IP 就大概率记错。
Node.js + ws 库:从 req.socket.remoteAddress 取 IP,但要注意 IPv6 和代理
Node.js 的 ws 库在 connection 回调中传入的 req 是原始 HTTP 请求对象,IP 信息藏在 socket 层:
-
req.socket.remoteAddress是最直接来源,但若前端走 Nginx 反代,默认只记录 Nginx 的内网 IP(如127.0.0.1) - IPv6 地址格式为
::ffff:192.168.1.100,需用.split("::ffff:").pop()剥离前缀 - 必须在 Nginx 配置中透传头字段:
proxy_set_header X-Forwarded-For $remote_addr;,否则X-Forwarded-For为空 - 若 Nginx 后还有 LB,
X-Forwarded-For会是逗号分隔的列表,取第一个非私有地址(如跳过10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)
示例代码片段:
wsServer.on('connection', (ws, req) => {
let ip = req.socket.remoteAddress;
if (req.headers['x-forwarded-for']) {
const ips = req.headers['x-forwarded-for'].split(',');
ip = ips[0].trim(); // 取最左端(客户端直连代理的那个 IP)
}
console.log(`Client IP: ${ip} — Connected at ${new Date().toISOString()}`);
});
Java Spring WebSocket:靠 HttpServletRequest 拿 IP,但需确保未被拦截器截断
Spring 的 @ServerEndpoint 不自动暴露 HttpServletRequest,必须通过 ServerEndpointConfig.Configurator 注入,或改用 WebSocketHandler 配合 HandshakeInterceptor。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 若用注解方式(
@ServerEndpoint),必须自定义Configurator,并在modifyHandshake中把HttpServletRequest存进HttpSession或userProperties - 常见坑:Spring Security 或 WebMvcConfigurer 的拦截器默认拦掉
/websocket路径,导致req为 null;需显式放行:registry.addEndpoint("/websocket").setAllowedOrigins("*").withSockJS(); -
req.getRemoteAddr()返回的是直连 IP,不是真实客户端 IP;仍要检查req.getHeader("X-Forwarded-For") - 不要依赖
req.getRemoteHost(),DNS 反查慢且不可靠,生产环境应禁用
libwebsockets C 服务端:用 lws_hdr_copy 解析代理头,手动校验私有网段
libwebsockets 在 LWS_CALLBACK_ESTABLISHED 回调中不提供现成 IP 字符串,所有头字段都得自己拷贝解析:
- 调用
lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_X_FORWARDED_FOR)获取原始头值 - 不能直接信任
X-Forwarded-For第一个 IP——攻击者可伪造该头;应在可信代理链最后一跳才采信(比如只信任来自10.10.0.0/16的转发) - 私有 IP 列表必须硬编码校验:
127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、fd00::/8(IPv6 ULA) - 若
WSI_TOKEN_X_FORWARDED_FOR为空,回退到lws_get_peer_addresses获取 TCP 对端地址(即直连 IP)
PHP(Ratchet / ReactPHP):ConnectionInterface 的 getRemoteAddress() 不可靠,必须结合 $_SERVER
Ratchet 的 onOpen(ConnectionInterface $conn) 中,$conn->getRemoteAddress() 返回的是 socket 层地址,和 Node.js 类似,受代理影响极大:
- Apache 环境下,真实 IP 在
$_SERVER['HTTP_X_FORWARDED_FOR']或$_SERVER['HTTP_X_REAL_IP'];Nginx 下同理 - 注意
$_SERVER键名大小写:Apache 默认转大写加HTTP_前缀,Nginx 默认小写,实际取决于 FastCGI 配置 - ReactPHP 的
ConnectionInterface没有内置 HTTP 头访问能力,必须用clue/reactphp-buzz或自定义握手中间件提前捕获请求上下文 - 日志里记 IP 时务必做脱敏,比如
192.168.1.123 → 192.168.1.xxx,避免审计风险
真实 IP 获取从来不是“调一个函数就完事”的事——它强依赖基础设施配置的一致性,少配一条 proxy_set_header,整条链路的日志就失效。别只盯着代码,先盯紧你前面那层反向代理。










