nginx 配置要领
你提出的问题非常好!确实在配置 WebSocket 代理时,proxy_pass
的 URL 处理需要特别注意。以下是关键解释和修正建议:
1. 原配置的问题分析
location /ws/ {proxy_pass http://fastapi-container:8000; # 会保留 /ws/ 前缀传递给后端
}
- 这种配置会将完整路径(包括
/ws/
)传递给 FastAPI 服务。 - 如果你的 FastAPI 的 WebSocket 路由是
/ws/chat
,实际请求会被转发为:
http://fastapi-container:8000/ws/chat
2. 两种正确的配置方式
方式 1:保留路径前缀(推荐)
location /ws/ {proxy_pass http://fastapi-container:8000; # 不写结尾的 /proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";
}
- 行为:
- 请求
wss://your-domain.com/ws/chat
- → 转发到
http://fastapi-container:8000/ws/chat
- 请求
- 适用场景:
FastAPI 的 WebSocket 路由需要完整路径(如@app.websocket("/ws/chat")
)
方式 2:去除路径前缀
location /ws/ {proxy_pass http://fastapi-container:8000/; # 结尾有 /proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";
}
- 行为:
- 请求
wss://your-domain.com/ws/chat
- → 转发到
http://fastapi-container:8000/chat
(去除了/ws
前缀)
- 请求
- 适用场景:
FastAPI 的 WebSocket 路由直接使用根路径(如@app.websocket("/chat")
)
3. 完整修正方案
情况 1:FastAPI 需要完整路径
location /ws/ {proxy_pass http://fastapi-container:8000; # 无结尾 /# 其他 WebSocket 头保持不变...
}# FastAPI 代码
@app.websocket("/ws/chat") # 匹配完整路径
async def websocket_endpoint(websocket: WebSocket):...
情况 2:FastAPI 不需要前缀
location /ws/ {proxy_pass http://fastapi-container:8000/; # 有结尾 /# 其他 WebSocket 头保持不变...
}# FastAPI 代码
@app.websocket("/chat") # 路径已去除 /ws
async def websocket_endpoint(websocket: WebSocket):...
4. 测试验证方法
-
检查实际转发路径
在 FastAPI 中添加日志:@app.websocket("/ws/chat") async def websocket_endpoint(websocket: WebSocket):print("Received WebSocket at:", websocket.url.path) # 查看实际路径
-
Nginx 调试日志
在 Nginx 配置中添加:error_log /var/log/nginx/error.log debug;
5. 常见误区
错误配置 | 结果 | 修正 |
---|---|---|
proxy_pass http://backend; + location /ws/ | 保留 /ws/ 前缀 | 符合预期 |
proxy_pass http://backend/; + location /ws/ | 去除 /ws/ 前缀 | 需调整 FastAPI 路由 |
proxy_pass http://backend/ws/; + location /ws/ | 重复 /ws/ws/ | 绝对避免 |
总结
- 关键规则:
proxy_pass
的 URL 以/
结尾 → 去除location
匹配的前缀proxy_pass
的 URL 无/
结尾 → 保留完整路径
- 推荐做法:
保持 Nginx 的location
和 FastAPI 路由路径一致,避免混淆。