Nginx 代理 WebSocket 必须使用 proxy_pass 而非 fastcgi_pass,因 FastCGI 不支持长连接、双向通信和协议升级;正确配置需包含 proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade"、proxy_read_timeout 和 proxy_buffering off 等关键项。

Nginx 本身不通过 FastCGI 支持 WebSocket,这是个关键前提。FastCGI 是为 CGI 类短生命周期脚本(如 PHP-FPM)设计的协议,它不支持长连接、双向通信和协议升级机制——而这三项正是 WebSocket 握手与运行所必需的。
WebSocket 必须走 HTTP 反向代理路径(proxy_pass),而非 fastcgi_pass。如果你在 Nginx 配置里看到 fastcgi_pass 用于 WebSocket 场景,那一定是误配,会导致握手失败(常见 400 Bad Request 或直接断连),且后端根本收不到 Upgrade 请求。
✅ 正确做法:用 proxy_pass 代理 WebSocket,彻底避开 FastCGI
WebSocket 服务(如基于 Node.js 的 ws、Socket.IO 后端,或 Python 的 uvicorn + websockets)应作为独立 HTTP 服务运行(例如监听 http://127.0.0.1:8080),Nginx 用标准反向代理方式转发,重点配置协议升级与连接保持:
proxy_http_version 1.1;
强制使用 HTTP/1.1 —— WebSocket 握手依赖其Upgrade机制。proxy_set_header Upgrade $http_upgrade;
将客户端发来的Upgrade: websocket头原样透传,不能写死为"websocket"(否则不兼容 STOMP/MQTT 等其他升级协议)。proxy_set_header Connection "upgrade";
注意是固定字符串"upgrade",不是$connection_upgrade(后者需配合map指令才安全,但直接写死更简洁可靠)。proxy_read_timeout 86400;
防止 Nginx 在空闲时主动断连;若后端每 30 秒发 ping,设为45即可,但绝不能 ≤30。proxy_send_timeout 86400;
与读超时匹配,避免大消息分片被中断。proxy_buffering off;
关闭缓冲,防止多个 WebSocket TEXT 帧被合并发送,引发粘包或延迟。proxy_set_header Host $host;
保证后端能正确识别原始域名。
示例配置:
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
proxy_buffering off;
}❌ 为什么 FastCGI 不行?
- FastCGI 协议无
Upgrade流程,无法响应101 Switching Protocols。 - FastCGI 连接是请求-响应式、一次一连,不维持 TCP 长连接。
- PHP-FPM 等 FastCGI 实现本身不处理 WebSocket 帧解析,也不提供
ws://或wss://服务能力。 - 即使强行把 WebSocket 请求发给 PHP-FPM,它只会返回 502 或静默失败,日志中往往只显示 “upstream prematurely closed connection”。
⚠️ 补充提醒:某些旧文档或博客提到“用 PHP + FastCGI 实现 WebSocket”,实际是混淆了概念——那类方案本质是用 PHP 轮询(Comet)或 SSE 模拟实时,不是真正的 WebSocket。
? 如果你用的是 PHP 应用且必须集成 WebSocket
你需要另起一个独立服务进程(如用 Ratchet、Swoole 或 Workerman 启动 WebSocket 服务器),监听单独端口(如 0.0.0.0:9501),然后让 Nginx 用 proxy_pass 去代理这个端口,不要把它塞进 FastCGI 流程里。
PHP 主站走 fastcgi_pass,WebSocket 子路径(如 /ws/)走 proxy_pass,二者共存完全可行。
不复杂但容易忽略


















