Nginx 代理 Ollama 流式接口:从踩坑到解决的完整指南
谢藏锋
最近在集成 Ollama 的流式生成功能时遇到了一个棘手的问题:通过 Nginx 代理后,原本应该逐字输出的回答变成了一次性完整返回。这个问题不仅影响用户体验,更让整个实时交互的设计失去了意义。经过一番调试和研究,我终于找到了症结所在,今天就来分享一下这个过程中的收获。
流式传输的本质与 Nginx 的冲突
首先我们需要理解,Ollama 的 stream 接口之所以能实现逐字输出,核心在于采用了分块传输编码(Chunked Transfer Encoding)。这种传输方式允许服务器边生成内容边向客户端发送,每个数据块独立传输,不需要预先知道整个响应的大小。
而 Nginx 作为反向代理的默认行为,却与此特性背道而驰。Nginx 会自动缓冲服务器响应,直到缓冲区填满或请求完成才会一次性发送给客户端 —— 这就是为什么代理后流式输出会变成完整输出的根本原因。这种机制在普通 Web 请求中能提高效率,但在需要实时交互的场景下就成了障碍。
关键配置项解析
解决问题的关键在于修改 Nginx 配置,让其放弃缓冲行为,忠实传递每一个数据块。经过测试,以下几个配置项必不可少:
location /ollama/ {
proxy_pass http://127.0.0.1:11434/;
# 核心配置:关闭缓冲机制
proxy_buffering off; # 禁止响应缓冲
proxy_cache off; # 关闭缓存功能
chunked_transfer_encoding on; # 保持分块传输编码
# 连接处理
proxy_set_header Connection ''; # 清除连接头,避免被修改
proxy_http_version 1.1; # 使用HTTP/1.1支持长连接
# 超时设置
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
其中proxy_buffering off是最关键的配置,它直接禁用了 Nginx 的响应缓冲。而chunked_transfer_encoding on则确保分块传输编码能正确传递到客户端。
长连接与超时设置的重要性
在调试过程中,我发现即使关闭了缓冲,有时流式传输仍会中途中断。这是因为 Nginx 默认的超时时间过短(通常是 60 秒),而大型模型的生成过程很容易超过这个时限。
将超时时间延长到合理范围(如 300 秒)能有效解决这个问题:
- proxy_connect_timeout:与后端服务建立连接的超时时间
- proxy_send_timeout:发送请求到后端的超时时间
- proxy_read_timeout:等待后端响应的超时时间
这些值的设置需要根据实际使用的模型和网络环境调整,对于生成类任务,建议设置为 300-600 秒。
请求头与响应头的正确传递
另一个容易被忽略的细节是请求头的处理。Ollama 的 stream 接口依赖特定的请求头来启用流式模式,因此需要确保 Nginx 正确传递这些头部信息:
# 传递关键请求头
proxy_pass_request_headers on;
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;
特别需要注意的是Content-Type响应头,流式接口通常使用application/x-ndjson或text/event-stream类型,Nginx 不应修改这些头部信息。
完整配置示例
整合以上所有要点,这里提供一个完整的 Nginx 配置示例:
server {
listen 80;
server_name ai.example.com;
location /ollama/ {
proxy_pass http://127.0.0.1:11434/;
# 流式传输核心配置
proxy_buffering off;
proxy_cache off;
proxy_pass_request_headers on;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding on;
# 超时设置
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# 头部设置
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;
}
}