使用Nginx代理WebSocket(尤其是WSS加密连接),最核心的注意事项是:必须显式配置协议升级(Upgrade)和超时时间,否则连接会因Nginx的默认HTTP短连接行为而失败或频繁断开。

以下是需要特别注意的几个方面:

一、升级协议头:必须透传,缺一不可

WebSocket握手依赖HTTP升级机制,Nginx默认不会透传相关头部。因此,必须在`location`块中强制配置以下三项:

```nginx

location /ws/ {

    proxy_pass http://backend_ws;        # 后端地址

    proxy_http_version 1.1;              # 1. 必须使用HTTP/1.1

    proxy_set_header Upgrade $http_upgrade; # 2. 透传Upgrade头

    proxy_set_header Connection "upgrade";  # 3. 设置为upgrade

}

```

   `proxy_http_version 1.1;`:WebSocket的升级机制仅在HTTP/1.1及以上版本中定义,HTTP/1.0不支持。

   `proxy_set_header Upgrade $http_upgrade;`:将客户端发来的`Upgrade: websocket`头原样传递给后端。此处应使用`$http_upgrade`变量,而非硬编码"websocket",以兼容其他升级协议。

   `proxy_set_header Connection "upgrade";`:这是最关键的一步。此处的值必须是带引号的固定字符串`"upgrade"`,而非变量。如果漏掉或配错,后端无法识别升级意图,连接会失败。

二、超时时间:大幅延长,避免“准时断开”

Nginx默认的`proxy_read_timeout`为60秒,这对于需要长期保持空闲的WebSocket连接来说太短了,会导致连接在无数据传输时被强制断开。必须调大相关超时参数:

```nginx

location /ws/ {

    # ... 升级协议头配置 ...

    proxy_read_timeout 86400s;  # 建议设为24小时或更长

    proxy_send_timeout 86400s;  # 与读超时保持一致

    proxy_buffering off;        # 禁用缓冲,保证实时性

}

```

   `proxy_read_timeout`:定义Nginx等待后端发送数据的超时时间。对于WebSocket,应设置为一个足够大的值(如`86400s`),或至少大于应用层的心跳间隔。

   `proxy_send_timeout`:定义Nginx向后端发送数据的超时时间。建议与`proxy_read_timeout`设置为相同值,防止大消息分片传输时被中断。

   `proxy_buffering off;`:必须关闭代理缓冲。WebSocket是流式通信,开启缓冲会导致数据帧被缓存,破坏实时性,甚至造成粘包。

SSL/WSS 特定注意事项

当代理WSS(WebSocket Secure)时,除了上述通用配置,还需注意以下几点:

   SSL 终端(SSL Termination):最常见的模式是Nginx处理SSL加密,然后将解密后的普通WebSocket(WS)流量转发给后端。这样后端服务无需处理证书,简化了架构。

   HTTPS 基础配置:确保`server`块正确监听了443端口并配置了SSL证书。

    ```nginx

    server {

        listen 443 ssl;

        server_name ws.yourdomain.com;

        ssl_certificate /path/to/your/certificate.pem;   # 证书文件

        ssl_certificate_key /path/to/your/private.key;  # 私钥文件

        # 推荐显式指定TLS版本

        ssl_protocols TLSv1.2 TLSv1.3; 

        # ... WebSocket location配置 ...

    }

    ```

   转发原始协议:为了让后端知道原始请求是HTTPS,可以添加 `proxy_set_header X-Forwarded-Proto $scheme;`。这有助于后端正确生成重定向链接等。

   HTTP/2 的兼容性:虽然可以在`listen`指令中启用`http2`,但必须保留 `proxy_http_version 1.1;`,因为WebSocket升级依赖于HTTP/1.1。

完整配置示例

以下是一个集成了上述所有要点的完整Nginx配置示例:

```nginx

# 映射 $connection_upgrade 变量,更优雅地处理升级头

map $http_upgrade $connection_upgrade {

    default upgrade;

    ''      close;

}

upstream websocket_backend {

    # 使用 ip_hash 保证同一客户端始终连接到同一后端

    ip_hash;

    server 127.0.0.1:8080;

    server 127.0.0.1:8081;

    keepalive 32; # 保持与后端的空闲连接池

}

server {

    listen 443 ssl http2;

    server_name ws.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/ws.yourdomain.com/fullchain.pem;

    ssl_certificate_key /etc/letsencrypt/live/ws.yourdomain.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;

    location /ws/ {

        proxy_pass http://websocket_backend;

        # WebSocket 核心配置

        proxy_http_version 1.1;

        proxy_set_header Upgrade $http_upgrade;

        proxy_set_header Connection $connection_upgrade; # 使用map变量

        # 传递真实IP和协议

        proxy_set_header Host $host;

        proxy_set_header X-Real-IP $remote_addr;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header X-Forwarded-Proto $scheme;

        # 超时与缓冲配置

        proxy_read_timeout 86400s;

        proxy_send_timeout 86400s;

        proxy_buffering off;

    }

}

```

 验证与调试

配置完成后,可以通过以下方法验证是否生效:

   检查响应状态码:在浏览器开发者工具的“网络”面板中,WebSocket握手请求的状态码必须是 `101 Switching Protocols`。如果是200或其他错误码,则说明升级失败。

   使用curl模拟:用以下命令模拟WebSocket握手,观察响应头中是否包含`Upgrade: websocket`和`Connection: upgrade`。

    ```bash

    curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" https://your-domain/ws/

    ```

   检查Nginx日志:如果连接异常断开,查看Nginx的错误日志(通常位于`/var/log/nginx/error.log`),搜索 `upstream prematurely closed connection` 等关键词,这通常指向超时或后端问题