Nginx配置SSE,WebSocket的转发请求
一、配置背景
(火狐游览器不支持,建议谷歌游览器开发测试)
在使用 Django 框架开发 Web 应用时,为了实现实时通信,可以选择使用以下两种技术:
SSE (Server-Sent Events): 适用于服务器向客户端推送单向数据流。
WebSocket: 适用于双向通信场景。
为了将 SSE 和 WebSocket 的请求正确转发到后端服务,Nginx 需要根据特定的请求路径进行配置。
SSE和WebSockets都能实现实时数据推送,但它们的设计目标不同。
| 特性 | SSE | WebSocket |
|---|---|---|
| 通信方式 | 单向(服务器 → 客户端) | 双向(客户端 ↔ 服务器) |
| 协议 | 基于HTTP(EventStream) | 自定义TCP协议 |
| 自动重连 | 浏览器内置支持 | 需要手动实现 |
| 兼容性 | 兼容HTTP代理、CDN | 可能受限于防火墙、代理 |
| 适用场景 | 服务器数据推送(新闻、日志) | 聊天、游戏、协作编辑 |
如果应用只需要服务器向客户端推送数据(如股票行情、新闻、社交通知),SSE是更好的选择。如果需要双向交互(如在线游戏、WebRTC、IM聊天),WebSockets更适合。
二、Nginx 配置示例
(一)基础配置
打开并编辑 Nginx 配置文件nginx.conf
# 定义变量用于处理 WebSocket 的 Upgrade 和 Connection 请求头:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name 192.168.1.9;
# 处理静态文件的路径
location /static/ {
alias /home/staticfiles/;
}
# 正常的接口请求转发到后端
location / {
proxy_pass http://127.0.0.1:8000; # 转发到后端
proxy_set_header Host $http_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;
}
# 转发 SSE 请求
location ~ ^/sse/ { # 匹配以 /sse/ 开头的路径
proxy_pass http://127.0.0.1:8000; # 配置代理的后端服务器地址
proxy_http_version 1.1; # 设置 HTTP 版本,SSE 需要 HTTP/1.1
proxy_set_header Connection ''; # 保持连接活性,不发送连接关闭的信号
proxy_buffering off; #不对 SSE 响应进行缓冲,直接透传给客户端
proxy_cache off; # 关闭代理缓存
proxy_read_timeout 3600s; # 设置代理读取服务器响应的超时时间
proxy_send_timeout 3600s;
proxy_connect_timeout 1h; # 设置客户端连接的超时时间
proxy_set_header Host $host; # 配置代理传递的头部,确保 Host 头部正确传递
proxy_set_header X-Accel-Buffering no; # 设置代理的响应头部,保持传输编码为 chunked
add_header Cache-Control no-cache;
# add_header X-Accel-Buffering "no";
chunked_transfer_encoding on; # 启用分块传输编码
gzip off;
# 设置跨域资源共享 (CORS),如果你的客户端和服务器不在同一个域上
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Origin,Authorization,Accept,X-Requested-With' always;
if ($request_method = 'OPTIONS') {
# 如果请求方法为 OPTIONS,则返回 204 (无内容)
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Origin,Authorization,Accept,X-Requested-With';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
}
# 转发 WebSocket 请求
location ^~ /ws/ {
proxy_pass http://127.0.0.1:8000; # 转发到 ASGI 服务器
# 保证连接不会超时
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_connect_timeout 3600s;
# 转发 WebSocket 请求头
proxy_set_header Host $http_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;
# WebSocket 特有配置
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# 可选: 传递 Authorization 头部,如果需要身份验证
# proxy_set_header Authorization $http_authorization;
}
}
-
proxy_buffering off;确保 Nginx 不会缓冲响应,而是直接将其发送给客户端,这对于 SSE 至关重要。- 启用(默认行为):当 proxy_buffering 设置为 on(或者没有明确设置,因为 on 是默认值)时,Nginx 会将来自上游服务器的响应内容缓存在本地内存中,直到整个响应体被接收完毕或者达到了 proxy_buffers 和 proxy_buffer_size 指令所指定的缓冲区大小限制。这样做的好处是可以减少与上游服务器的交互次数,提高响应速度,并允许 Nginx 对响应内容进行一些处理(如 gzip 压缩)后再发送给客户端。
- 禁用(proxy_buffering off;):当设置为 off 时,Nginx 会将来自上游服务器的响应内容直接发送给客户端,而不会进行本地缓存。这意味着客户端会更快地开始接收数据,特别是对于那些需要快速开始接收响应首部的场景(如流媒体或实时数据服务)很有用。但是,这也可能导致客户端的响应时间更加依赖于上游服务器的响应速度和稳定性,因为 Nginx 不再对响应进行本地缓冲和预处理。
-
proxy_read_timeout设置了 Nginx 等待后端服务器响应的超时时间。SSE 需要长时间的连接,所以这个值设置得比较高。 -
proxy_connect_timeout设置了 Nginx 尝试与后端服务器建立连接的超时时间。 -
proxy_http_version 1.1;指定使用 HTTP/1.1,因为 SSE 需要这个版本的协议。 -
proxy_set_header Connection '';确保 Nginx 不会发送Connection: close头部,这对于保持长连接至关重要。 -
proxy_pass指向后端服务的地址,这里应该替换为你的后端服务器地址。 -
proxy_set_header X-Accel-Buffering no;确保 Nginx 不会对此连接进行缓冲。 -
CORS 相关的头部设置允许不同的源请求资源。这在你的服务需要被不同域的客户端访问时很有用。
-
proxy_cache 对于 SSE(Server-Sent Events)连接,通常不建议启用 Nginx 的代理缓存(proxy_cache)。因为 SSE 是一种长连接技术,它通过保持持久连接来实时推送数据给客户端,而代理缓存会将响应数据缓存起来并在后续请求中返回缓存的响应,这与 SSE 的工作方式相违背。如果启用代理缓存,Nginx 可能会缓存 SSE 的数据,并在后续的连接中返回相同的缓存数据,这样会导致客户端收到重复的消息,破坏了 SSE 的实时性和准确性。
-
chunked_transfer_encoding 参数可以根据你的需求决定是否关闭。在 SSE 中,通常不需要禁用分块传输编码,因为它允许将数据以数据块的形式逐步传输,与 SSE 的流式数据特性相符合。
-
客户端与服务器之间有n个nginx,就要至少在n-1个nginx里面配置好这个header。=> (add_header X-Accel-Buffering "no"😉
三、配置说明
(一)SSE 配置
路径匹配:
使用 location ~ ^/sse/ 匹配所有以 /sse/ 开头的路径。
实时性优化:
proxy_buffering off: 禁用缓冲,确保数据流能及时推送到客户端。
proxy_read_timeout 3600s: 设置长时间超时,避免连接因长时间无活动而被关闭。
HTTP 版本:
强制使用 HTTP 1.1,以支持持久连接。
(二)WebSocket 配置
路径匹配:
使用 location ^~ /ws/ 匹配所有以 /ws/ 开头的路径。
升级请求:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
确保请求头包含 Upgrade 和 Connection 字段,以支持 WebSocket 协议。
超时设置:
proxy_read_timeout 和 proxy_send_timeout 设置为 300 秒,防止因无活动而断开连接。
HTTP 版本:
强制使用 HTTP 1.1,以支持 WebSocket 的双向通信。
有两种写法
第一种写法:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
location ^~ /ws/{
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
第二种用法:
server {
location ^~ /ws/{
proxy_set_header Upgrade $http_upgrade; # 设置 Upgrade 头
proxy_set_header Connection "upgrade"; # 设置 Connection 头
}
}
四、总结
通过上述配置,Nginx 能够:
根据请求路径匹配规则,将普通 HTTP 请求、SSE 请求和 WebSocket 请求分别转发到对应的后端服务。为 SSE 和 WebSocket 提供必要的配置,确保连接的稳定性和实时性。
五、sse效果出不来排查思路
1. 确保 Nginx 正确代理 SSE 请求
Nginx 默认会尝试缓冲响应内容,而 SSE 是流式输出,不能被缓冲。你需要确保 proxy_buffering 关闭:
location /sse {
proxy_pass http://backend_server;
proxy_buffering off; # 关闭缓冲
proxy_cache off; # 关闭缓存
proxy_set_header Connection ''; # 确保 Nginx 不会断开 SSE 连接
chunked_transfer_encoding on; # 确保传输支持 chunked encoding
}
检查点:
proxy_buffering offproxy_cache offConnection ''chunked_transfer_encoding on
2. 检查后端是否正确返回 SSE 响应
后端必须满足以下 SSE 规范:
Content-Type设置为text/event-stream- 以
data:开头发送数据,并以\n\n结尾 - 保持 HTTP 连接不关闭
你可以用 curl 测试后端 SSE 响应:
curl -v http://backend_server/sse
如果返回:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Transfer-Encoding: chunked
并且输出类似:
data: Hello World
data: Another event
说明后端是正常的。
如果没有 Content-Type: text/event-stream,那就要检查后端是否正确设置了这个头。
3. 确保 HTTP 连接不会被 Nginx 过早关闭
Nginx 可能因为超时关闭 SSE 连接,需要调整超时参数:
location /sse {
proxy_pass http://backend_server;
proxy_buffering off;
proxy_cache off;
proxy_set_header Connection '';
# 防止超时断开
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
keepalive_timeout 3600s;
}
检查点:
proxy_read_timeout:防止 Nginx 关闭长连接。proxy_send_timeout:确保 SSE 连接能长期保持。keepalive_timeout:保持客户端连接,防止过早关闭。
4. 确保 Nginx 没有 gzip 压缩 SSE
SSE 不支持 gzip 压缩,如果 Nginx 试图压缩流式数据,可能会导致 SSE 失效。确保你没有启用 gzip:
location /sse {
proxy_pass http://backend_server;
proxy_buffering off;
gzip off;
}
检查点:
gzip off;关闭压缩,SSE 不能被 gzip 处理。
5. 检查 WebSocket 代理是否干扰 SSE
如果你的应用同时使用 SSE 和 WebSocket,Nginx 可能会错误地将 SSE 请求当作 WebSocket 连接进行处理。你可以在 location /sse 中明确指定 不使用 WebSocket:
proxy_set_header Upgrade '';
proxy_set_header Connection '';
检查点:
proxy_set_header Upgrade '';避免 Nginx 误将 SSE 当作 WebSocket 代理。
6. 检查浏览器 SSE 连接是否被阻止
有些浏览器可能因为 CORS 或代理问题阻止 SSE 连接,检查 Chrome 开发者工具 (F12) → Network → SSE 请求状态:
- 状态码 200:正常
- 状态码 403/404:检查 Nginx 配置或后端
- ERR_CONNECTION_CLOSED:可能是 Nginx 或后端关闭了连接
可以在 Nginx 端允许 CORS:
location /sse {
add_header Access-Control-Allow-Origin *;
add_header Cache-Control no-cache;
}
7. 检查防火墙 & 代理
如果 SSE 连接在 生产环境不可用,但本地测试正常:
- 检查防火墙是否关闭长连接
- 检查 CDN 或反向代理是否缓存或阻断 SSE
如果使用 Cloudflare 或某些代理,可能需要手动关闭 HTTP 缓存或开启 WebSocket 兼容模式。
8. 确保前端正确处理 SSE
前端代码示例:
const eventSource = new EventSource("/sse");
eventSource.onmessage = function(event) {
console.log("Received:", event.data);
};
eventSource.onerror = function(err) {
console.error("SSE error:", err);
};
如果 eventSource 立即 onerror,通常是 Nginx 配置问题。
总结
✅ 最小化 Nginx SSE 代理配置:
location /sse {
proxy_pass http://backend_server;
proxy_buffering off;
proxy_cache off;
gzip off;
proxy_set_header Connection '';
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
✅ 确保后端返回正确的 SSE 响应
Content-Type: text/event-stream- 以
data:开头,每条消息以\n\n结尾 - HTTP 连接持续不关闭
✅ 使用 curl 检查后端
curl -v http://backend_server/sse
✅ 浏览器开发者工具 (F12) 查看 SSE 请求状态
- 200 OK:正常
- 403/404:权限或路由问题
- ERR_CONNECTION_CLOSED:可能是超时或 Nginx 关闭连接
如果仍然有问题,可以打开 Nginx 日志:
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log
这样你就能定位 SSE 在 Nginx 中不能生效的原因

浙公网安备 33010602011771号