解决http-proxy-middleware无法捕获WebSocket响应问题及实现请求响应拦截与外部服务调用方案
修复WebSocket代理的响应拦截问题
问题根源
你遇到的proxyRes钩子不触发的核心原因是:WebSocket的握手响应(101 Switching Protocols)不会触发常规的proxyRes回调。http-proxy库对WebSocket的升级流程有单独的处理逻辑,常规的proxyRes仅针对普通HTTP请求的响应,不会覆盖WebSocket握手阶段的响应处理。
另外你的代码里还有一个变量作用域的小问题:在if(isWebSocketSuccess)块里重新用const声明了requestId,导致外部的requestId变量始终是undefined,即使后端返回了xyz-req-id头也无法正确获取。
修复方案
我们需要在proxyReqWs钩子中监听后端返回的响应(通过proxyReq的response事件),在这里完成响应头的拦截、修改,以及request-id的提取。
修改后的setupProxy函数代码如下:
// proxy.js export const setupProxy = (proxyConfig: ProxyConfig): RequestHandler => { const target = proxyConfig.endpoint; const proxyOptions: Options = { target: target, changeOrigin: true, secure: true, ws: true, on: { proxyReq: (proxyReq, req, res) => { log('--------------------------------------'); logRequest(req as Request, 'Original Request from Client'); // Log original client request proxyReq.setHeader('Authorization', `Bearer ${proxyConfig.details.apiKey}`); logRequest(proxyReq, 'MODIFIED request to Backend'); // Log modified request }, proxyReqWs: (proxyReq, req, socket, options, head) => { log('proxyReqWs event triggered for:', { method: req.method, url: req.url }); proxyReq.setHeader('Authorization', `Bearer ${proxyConfig.details.apiKey}`); // 监听后端返回的WebSocket握手响应 proxyReq.on('response', (proxyRes) => { logResponse(proxyRes, req as Request, 'Original Response from Backend'); // Log response from Backend const isWebSocketSuccess = proxyRes.statusCode === 101; let requestId: string | undefined; if(isWebSocketSuccess) { // 修复变量作用域问题:不要重新声明requestId requestId = proxyRes.headers['xyz-req-id'] as string | undefined; } if (requestId) { log(`Extracted request-id: ${requestId}`); // 修改响应头 proxyRes.headers['x-xyz-request-id'] = requestId; } delete proxyRes.headers['xyz-req-id']; logResponse(proxyRes, req as Request, 'MODIFIED response to Client'); // Log modified response log('--------------------------------------'); // 可选:调用外部服务处理request-id // 注意:这里如果是异步操作,不要阻塞响应,建议用非阻塞的方式 // 例如: // fetch('https://your-external-service.com/log', { // method: 'POST', // body: JSON.stringify({ requestId }), // headers: { 'Content-Type': 'application/json' } // }).catch(err => logError('External service call failed:', err)); }); }, error: (err, req, res) => { logError('Proxy Error:', err.message, 'Target:', target); if (res.writeHead && !res.headersSent) { res.writeHead(502, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Bad Gateway', message: err.message })); } } }, router: (req) => { return proxyConfig.endpoint; } }; return createProxyMiddleware(proxyOptions); };
关键修改点说明
- 监听
proxyReq的response事件:在proxyReqWs中,proxyReq是发送给后端的HTTP请求对象,当后端返回握手响应时,会触发response事件,我们在这里处理响应头。 - 修复变量作用域问题:移除了
if块内的const声明,直接给外部的requestId赋值,确保能正确提取并使用后端返回的xyz-req-id。 - 异步调用外部服务的注意事项:如果需要在拿到
requestId后调用外部服务,建议使用非阻塞的异步操作(比如fetch或axios的异步调用),不要用同步代码阻塞WebSocket握手流程,避免客户端超时。
验证方式
修改完成后,重新启动代理服务器,发起WebSocket连接请求:
- 你应该能看到
proxyReqWs的日志,以及response事件中打印的前后响应日志。 - 在浏览器开发者工具的网络面板中,客户端收到的响应头应该包含
x-xyz-request-id,且不再有xyz-req-id。
内容的提问来源于stack exchange,提问作者S7H
相关产品推荐
相关产品推荐

