今天遇到一个线上服务卡顿的问题,具体是外部访问经过 Nginx 代理的 MCP (Model Context Protocol) SSE (Server-Sent Events) 端点时,连接会一直卡住。排查与解决问题的过程记录如下,希望能给遇到类似场景的伙伴一些参考。
故障现象
该服务前端配置了一个 Nginx 作为反向代理,初始配置如下:
server {
listen 8081;
server_name xx.xx.xx;
location / {
proxy_pass http://svc-bot-prod:12213;
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_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
proxy_buffer_size 128k;
proxy_buffers 8 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;
}
}
直接访问服务的根路径 https://xx.xx.xx/ 是正常的,返回预期的 JSON 响应。

但是,当访问 SSE 端点 https://xx.xx.xx/sse 时,浏览器会一直处于加载状态,请求被挂起。

排查过程
首先,我们怀疑是后端服务本身的问题。于是进入服务 Pod 内部以及从 Nginx Pod 内部直接向后端服务发起测试:
sh-4.4$ curl http://127.0.0.1:12213/sse
event: endpoint
data: /message?sessionId=1oVX80qW7YS5ZdKgUdfnv
sh-4.4$ curl http://svc-bot-prod:12213/sse
event: endpoint
data: /message?sessionId=1oVX80qW7YS5ZdKgUdfnvQ
测试结果表明,绕过 Nginx 代理直接访问后端服务的 /sse 端点,SSE 流能够立即返回数据,连接是正常的。这说明后端服务本身没有问题。
接着,查看后端服务的 Pod 日志,发现大量与 “canceled” 相关的记录,时间点恰好与外部访问卡住的时间吻合:
2026-01-29 11:29:34.8595|INFO|ModelContextProtocol.Server.McpServer|Server (WebAPI 1.0.0.0) message processing canceled.
2026-01-29 11:29:34.8595|INFO|200|ModelContextProtocol.Server.McpServer|||Server (WebAPI 1.0.0.0) message processing canceled.-|chatbot-porta-backend-prod-788b885fdf-w2btf||GET-|curl/7.61.1|ModelContextProtocol.McpSessionHandler.LogEndpointMessageProcessingCanceled
2026-01-29 11:32:25.6742|INFO|200|ModelContextProtocol.Server.McpServer|||Server (WebAPI 1.0.0.0) message processing canceled.-|chatbot-porta-backend-prod-788b885fdf-w2btf||GET-|Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0|ModelContextProtocol.McpSessionHandler.LogEndpointMessageProcessingCanceled
2026-01-29 11:32:25.6742|INFO|ModelContextProtocol.Server.McpServer|Server (WebAPI 1.0.0.0) message processing canceled.
...
日志显示连接被取消,这通常是代理或客户端主动中断了连接。结合之前“直连正常、代理后卡住”的现象,问题根源指向了 Nginx 的默认代理行为与 SSE 这种长连接、流式响应协议的不兼容。
问题根因与解决方案
Nginx 默认的 proxy_buffering 是开启的,它会缓冲上游服务器(即后端应用)的响应数据。对于普通的 HTTP 请求,这可以提高性能。但对于 Server-Sent Events 这种需要实时、持续推送数据的流式连接来说,缓冲机制是致命的:
- 实时性失效:Nginx 会等待接收完一定量的数据(缓冲区满)或连接关闭后,才将数据一次性发送给客户端,导致客户端长时间收不到任何消息。
- 连接超时:由于数据被缓冲,连接看起来处于“静默”状态,可能触发客户端或 Nginx 自身的超时设置,最终断开连接,体现在后端日志中就是 “canceled”。
因此,解决方案的核心是为 SSE 端点配置独立的 Nginx location,并关闭缓冲和相关缓存。
以下是为 /sse 路径添加的专用配置:
location /sse {
# 配置代理的后端服务器地址
proxy_pass http://svc-bot-prod:12213;
# 关键配置1:关闭响应缓冲,实现数据实时透传
proxy_buffering off;
# 关键配置2:关闭缓存,防止响应被缓存
proxy_cache off;
# 设置长超时,适应SSE长连接特性
proxy_read_timeout 3600s;
proxy_connect_timeout 3600s;
# SSE 需要 HTTP/1.1
proxy_http_version 1.1;
# 清除 Connection 头,避免发送关闭信号
proxy_set_header Connection '';
# 传递必要的代理头部
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 关键配置3:禁用加速缓冲(X-Accel-Buffering),确保流式输出
proxy_set_header X-Accel-Buffering no;
}
配置要点解析:
proxy_buffering off;:这是最重要的指令,它告诉 Nginx 不要缓冲来自后端服务的响应体,收到数据后立即转发给客户端。
proxy_cache off;:作为双重保险,防止任何层级的缓存干扰流式输出。
proxy_http_version 1.1; 和 proxy_set_header Connection '';:确保使用 HTTP/1.1 并保持连接持久化,这是 SSE 协议的基础。
proxy_set_header X-Accel-Buffering no;:这个头部对 Nginx 自身尤其重要,它会覆盖可能存在的 proxy_buffering 默认值,强制禁用缓冲。
- 超时设置:将
proxy_read_timeout 和 proxy_connect_timeout 设置为一个很大的值(如3600秒),以避免在长连接空闲期间被意外断开。
验证结果
在 Nginx 配置中添加上述 /sse 的 location 块,并执行 nginx -s reload 重载配置后,再次从外部访问 https://xx.xx.xx/sse。

如图所示,SSE 连接立即建立,并成功收到了 event: endpoint 事件数据,问题得到解决。后端服务的 “canceled” 日志也随即消失。
总结
当使用 Nginx 代理 SSE、WebSocket 等长连接或流式服务时,必须特别注意其默认的缓冲行为。通用代理配置往往无法满足这类场景的实时性要求。最佳实践是针对具体的流式端点路径,配置独立的 location 块,并明确关闭缓冲和缓存,同时调整超时和 HTTP 版本设置。
这次排查也提醒我们,面对“内部正常、外部异常”的网络问题时,中间件(如 Nginx、API 网关)的配置是需要优先排查的方向。希望这个案例能帮到你。如果你在部署微服务或处理高并发架构时遇到其他问题,欢迎来 云栈社区 交流探讨。