在 nginx 中配置 websocket 代理时,如果客户端收到 400 bad request 错误,通常是因为 nginx 没有正确识别 websocket 的升级请求。解决这个问题的核心做法是:在 nginx 的配置文件中,为对应的 location 块添加特定的 http 头设置,将 `upgrade` 和 `connection` 头传递给后端服务,并显式指定 http/1.1 协议版本。

Nginx 配置中必须包含哪些关键指令
要让 Nginx 正确处理 WebSocket 连接,必须在 location 块中配置三个关键指令:proxy_http_version、proxy_set_header Upgrade 和 proxy_set_header Connection。默认情况下,Nginx 使用 HTTP/1.0 处理代理请求,且会自动清理一些头部信息,这会导致 WebSocket 握手失败。通过显式设置这些参数,可以确保 Nginx 将升级请求原样转发给后端应用服务器。

server {
listen 80;
location /ws/ {
proxy_pass http://backend_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
上述配置片段展示了标准的 WebSocket 代理设置。当客户端发起带有 Upgrade: websocket 头的请求时,Nginx 会根据 $http_upgrade 变量的值动态调整 Connection 头。如果变量为空,则保持普通 HTTP 连接;如果包含 websocket,则设置为 upgrade。这种机制确保了 WebSocket 连接建立后,Nginx 能够维持长连接而不中断。
为什么有时需要检查后端服务的响应
即使 Nginx 配置正确,如果后端应用服务器没有正确响应 WebSocket 握手,客户端仍可能收到 400 或 502 错误。后端服务必须返回状态码 101 Switching Protocols,并在响应头中包含 Upgrade: websocket 和 Connection: Upgrade。如果后端返回其他状态码,如 400 或 403,说明握手阶段被拒绝,问题出在后端逻辑而非 Nginx 配置。

# 后端 Python 示例 (使用 aiohttp)
import asyncio
from aiohttp import web
async def websocket_handler(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
async for msg in ws:
if msg.type == web.WSMsgType.TEXT:
await ws.send_str(f"Echo: {msg.data}")
elif msg.type == web.WSMsgType.ERROR:
print(f'WebSocket connection closed with exception {ws.exception()}')
return ws
app = web.Application()
app.router.add_get('/ws/', websocket_handler)
if __name__ == '__main__':
web.run_app(app, host='0.0.0.0', port=8080)
以上 Python 代码展示了一个简单的 WebSocket 服务端实现。它使用 aiohttp 库处理 WebSocket 连接。当 Nginx 转发请求到 /ws/ 路径时,后端会准备一个 WebSocket 响应并返回 101 状态码。如果后端代码中缺少 await ws.prepare(request) 或返回了错误的状态码,Nginx 将无法完成协议升级,导致客户端报错。
如何验证 WebSocket 连接是否成功建立
验证 WebSocket 连接是否成功,可以通过检查 Nginx 访问日志中的状态码和后端日志中的连接事件。如果 Nginx 日志显示 101 状态码,表示协议升级成功;如果显示 400 或 502,则需要检查配置或后端服务。此外,可以使用浏览器开发者工具的网络面板查看 WebSocket 连接的握手过程,确认 Upgrade 和 Connection 头是否正确传递。
在实际排查中,建议先确认 Nginx 配置中的 proxy_pass 地址是否正确指向后端服务,然后检查后端服务是否正在监听正确的端口。如果后端服务运行正常但 Nginx 仍返回错误,可以尝试在 Nginx 配置中添加 error_log 指令,记录详细的调试信息,以便定位具体问题。


















