你提出的問題非常好!確實在配置 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 路由路徑一致,避免混淆。