在生产环境中部署实时通信服务时,nginx 作为反向代理是不可或缺的基础设施。然而,websocket 协议与传统的 http 请求在底层机制上存在本质差异,直接套用常规的 nginx 代理配置必然导致连接失败或频繁断开。
一、 标准配置示例
以下是一套经过生产环境验证的 nginx websocket 代理最小完备配置。建议将 websocket 路由与常规 http api 路由隔离,分配独立的 location 块以便独立管理生命周期。
# 【全局配置】定义连接升级变量的映射关系(必须放在 http {} 块内)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# 【上游配置】定义后端 websocket 服务集群
upstream ws_backend {
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
keepalive 64;
}
server {
listen 443 ssl;
server_name ws.example.com;
# ssl 配置(生产环境强制使用 wss)
ssl_certificate /etc/nginx/ssl/ws.example.com.pem;
ssl_certificate_key /etc/nginx/ssl/ws.example.com.key;
location /ws/ {
proxy_pass http://ws_backend;
# ========== 核心三要素(缺一不可)==========
proxy_http_version 1.1;
proxy_set_header upgrade $http_upgrade;
proxy_set_header connection $connection_upgrade;
# ========== 元数据透传 ==========
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 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_request_buffering off;
}
}
二、 核心参数逐项解析
理解上述配置中的每一个指令,是排查问题和深度调优的前提。下面按功能模块对关键参数进行结构化讲解。
1. 协议升级相关参数
这是 nginx 支持 websocket 的根基,缺少任何一项都会导致握手失败。
| 参数 | 默认值 | 作用说明 | 为什么必须配置 |
|---|---|---|---|
proxy_http_version | 1.0 | 指定 nginx 与上游服务器通信使用的 http 协议版本 | websocket 握手依赖 http/1.1 的 upgrade 机制,http/1.0 不支持该机制,上游收不到升级请求头会直接返回 400 |
proxy_set_header upgrade | 无 | 将客户端请求中的 upgrade 头原样透传给上游 | nginx 默认不会转发 upgrade 和 connection 这两个逐跳头(hop-by-hop header),必须显式传递 |
proxy_set_header connection | 无 | 根据客户端请求动态设置发往上游的 connection 头 | 不能写死为 upgrade,否则普通 http 请求(如健康检查)也会被标记为升级请求,导致上游解析异常 |
map $http_upgrade ... | 无 | 定义变量映射规则:有 upgrade 头时值为 upgrade,否则为 close | 配合上一条指令实现智能路由,让同一个 location 同时兼容 websocket 和普通 http 请求 |
重点提示:map 指令必须放置在 http {} 块的顶层,不能放在 server 或 location 内部,否则 nginx 启动时会报语法错误。
2. 超时控制参数
websocket 是持久长连接,默认的超时配置完全不适用,这是生产环境中连接“无故断开”的最常见原因。
| 参数 | 默认值 | 作用说明 | 生产调优建议 |
|---|---|---|---|
proxy_read_timeout | 60s | 两次连续读取上游响应之间的最大空闲等待时间 | 根据业务心跳间隔设置,通常为心跳周期的 2~3 倍。若链路中有 cdn/slb,必须大于中间件的空闲超时时间 |
proxy_send_timeout | 60s | 两次连续向上游发送请求之间的最大空闲等待时间 | 与 proxy_read_timeout 保持一致,防止单向数据传输时另一端超时断连 |
proxy_connect_timeout | 60s | 与上游建立 tcp 连接的超时时间 | 一般保持默认即可,若后端在高负载下握手慢可适当调大至 10~30s |
避坑指南:proxy_read_timeout 不是“连接的总存活时间”,而是“两次数据帧之间的最大空闲间隔”。只要客户端或服务端在超时时间内发送了任意数据(包括 ping/pong 心跳帧),计时器就会重置。因此,应用层心跳是维持长连接的必要手段,仅靠调大超时值无法从根本上解决问题。
3. 缓冲与性能参数
websocket 要求低延迟的实时数据传输,默认的缓冲机制会严重破坏这一特性。
| 参数 | 默认值 | 作用说明 | 为什么必须关闭 |
|---|---|---|---|
proxy_buffering | on | 控制是否缓存上游的响应体 | 开启时 nginx 会将上游数据攒够一定量再发给客户端,导致消息延迟、乱序,违背实时通信的设计初衷 |
proxy_request_buffering | on | 控制是否缓存客户端的请求体 | websocket 双向通信,客户端发送的数据同样需要实时透传,不能缓冲 |
keepalive (upstream) | 无 | 维持 nginx 到上游的空闲 tcp 连接池大小 | 减少握手阶段的 tcp 三次握手和 tls 握手开销,提升连接建立速度。注意:ws 连接建立后会被独占,此连接池主要服务于握手和健康检查 |
4. 元数据透传参数
这些参数确保后端服务能够获取真实的客户端信息,是安全审计和业务逻辑的基础。
| 参数 | 作用说明 | 注意事项 |
|---|---|---|
host $host | 透传原始域名 | 后端框架通常依赖 host 头进行路由匹配和跨域校验,不透传会导致 404 或 403 |
x-real-ip $remote_addr | 传递客户端真实 ip | 仅传递直连 nginx 的 ip,若 nginx 前还有 lb,需改用 x-forwarded-for |
x-forwarded-for $proxy_add_x_forwarded_for | 追加客户端 ip 到已有链路链 | 多级代理时必须使用,保留完整的访问链路溯源信息 |
x-forwarded-proto $scheme | 传递原始协议(http/https) | 后端判断是否启用 wss、生成回调 url 时依赖此头 |
三、 生产环境必做的三项加固
掌握了基础参数后,还需要针对生产环境的特殊性进行加固,避免上线后出现安全事故或性能瓶颈。
1. 并发连接数限制(防资源耗尽攻击)
恶意用户可以轻易建立大量 websocket 连接耗尽服务器文件描述符。必须在 nginx 层设置单 ip 并发上限:
# http 块中定义共享内存区域 limit_conn_zone $binary_remote_addr zone=ws_conn_limit:10m; # location 块中应用限制 limit_conn ws_conn_limit 20; # 单 ip 最多 20 个并发 ws 连接 limit_conn_status 503; # 超限返回 503 service unavailable
2. 握手速率限制(防 cc 攻击)
高频的连接/断开操作比持久连接更消耗 cpu 资源,需限制握手请求频率:
# http 块中定义限速区域 limit_req_zone $binary_remote_addr zone=ws_req_limit:10m rate=10r/s; # location 块中应用限速 limit_req zone=ws_req_limit burst=20 nodelay;
3. 系统内核参数调优
每个 websocket 连接占用一个文件描述符,万级并发下默认内核参数会成为瓶颈。需在 /etc/sysctl.conf 中调整:
# 提升系统级文件描述符上限 fs.file-max = 1048576 fs.nr_open = 1048576 # 扩大本地端口范围,防止 nginx 连接上游时端口耗尽 net.ipv4.ip_local_port_range = 1024 65535 # 优化 tcp keepalive 作为应用层心跳的兜底 net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_intvl = 30 net.ipv4.tcp_keepalive_probes = 3
修改后执行 sysctl -p 生效,同时确保 nginx 配置中 worker_rlimit_nofile 的值大于等于 worker_connections × worker_processes。
四、 常见故障速查表
当 websocket 连接异常时,可根据以下对照表快速定位问题根因:
| 现象 | 可能原因 | 排查与解决方案 |
|---|---|---|
| 握手返回 400 | 上游未收到 upgrade 头 | 检查是否遗漏 proxy_http_version 1.1;抓包确认 nginx 发往后端的请求头 |
| 握手返回 502 | 后端拒绝连接或握手完成前断开 | 检查后端服务是否存活、端口是否正确;查看后端应用日志是否有鉴权/路径匹配失败 |
| 连接建立后固定时间断开 | 中间件空闲超时 | 确认链路中 cdn/slb/waf 的空闲超时时间,确保 proxy_read_timeout 大于该值,并实现应用层心跳 |
| 消息延迟或乱序 | 代理缓冲未关闭 | 检查 proxy_buffering off 和 proxy_request_buffering off 是否生效 |
| 高并发下新连接被拒绝 | 文件描述符或端口耗尽 | 检查 ulimit -n、worker_rlimit_nofile、ip_local_port_range 是否已调优 |
| 后端报 broken pipe | nginx 先于应用层断开连接 | 确保 nginx 的 proxy_read_timeout 大于后端应用层设置的读超时时间 |
到此这篇关于nginx反向代理websocket的配置详解与参数实战指南的文章就介绍到这了,更多相关nginx反向代理websocket内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论