Nginx WebSocket 代理

WebSocket 让浏览器与服务器维持一条全双工长连接,聊天、实时推送、协同编辑都依赖它。它先走普通 HTTP 握手,成功后协议升级为 ws,Nginx 反代时需要把升级相关的请求头原样透传给后端。

升级原理

客户端在握手请求里带上 Upgrade: websocket 和 Connection: Upgrade,服务端同意后返回 101 Switching Protocols,此后这条连接不再按 HTTP 语义解析,而是双向转发原始帧。Nginx 从 1.3.13 起支持 WebSocket 反代,关键是把这两个头透传、并让转发使用 HTTP/1.1。

基本配置

先定义 map 决定 Connection 头取值,再在 location 里透传升级头并调长超时:

map $http_upgrade $connection_upgrade {
    default upgrade;        # 客户端要求升级时转发 upgrade
    ''      close;          # 普通请求则置 close,避免误伤 keepalive
}

upstream ws_backend {
    server 127.0.0.1:8081;  # Node/Java 等后端 WebSocket 服务
}

server {
    listen 80;
    server_name ws.example.com;

    location /ws/ {
        proxy_pass http://ws_backend;
        proxy_http_version 1.1;        # 升级依赖 HTTP/1.1,默认 1.0 不行
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_read_timeout 3600s;      # 空闲超时调长(默认 60s 会掐断空闲连接)
        proxy_send_timeout 3600s;
    }
}

用 curl 模拟一次完整握手验证(返回 101 即升级成功):

curl -i -N --max-time 5 \
  -H "Connection: Upgrade" -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
  -H "Sec-WebSocket-Version: 13" http://example.com/ws/
# 输出:HTTP/1.1 101 Switching Protocols

常见坑

现象原因处理
连接频繁断开proxy_read_timeout 默认 60s,空闲即被掐调长超时,或客户端按周期发心跳
后端只收到普通 GET,升级失败多级代理链路中某层丢了 Upgrade/Connection 头每一层代理都要重新透传这两个头
一直 400/502忘了 proxy_http_version 1.1显式声明 1.1
握手成功但很快 502后端服务未监听或 upstream 端口写错核对端口与进程

小结

WebSocket 反代四件套:map 生成 Connection 头、proxy_http_version 1.1、透传 Upgrade/Connection、调长读写超时。遇到断连先查超时与心跳,多级链路则逐层确认头部是否透传。

笔记加载中…