Nginx 反向代理配置避坑指南(proxy_pass 斜杠、502排查、CORS、文件上傳)
文章導讀:本文系統(tǒng)整理 Docker 環(huán)境下 Nginx 反向代理配置的高頻踩坑場景,涵蓋 proxy_pass 末尾斜杠行為差異、502/504 三大排查路徑、upstream keepalive 配置、Host 與真實 IP 透傳、文件上傳 413 處理、CORS 完整配置及 location 匹配優(yōu)先級,每個問題給出可直接使用的配置片段。適合正在排查 Nginx 反向代理問題或準備上生產(chǎn)的開發(fā)/運維人員。
一、改配置前必做:nginx -t 驗證
每次修改 Nginx 配置后,必須先執(zhí)行語法驗證再 reload,避免配置錯誤導致 reload 失敗、服務(wù)無法恢復(fù)。
# 在運行中的容器里驗證 docker exec nginx-container nginx -t # CI/CD 場景:用臨時容器驗證,不依賴運行中的服務(wù) docker run --rm \ -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \ nginx:1.26.2-alpine nginx -t
驗證通過后再 reload,一行命令串聯(lián):
docker exec nginx-container nginx -t && docker exec nginx-container nginx -s reload
注意:Nginx 報錯提示的行號有時與實際出錯位置偏移幾行,配合錯誤描述文字來定位,不要只看行號。
二、proxy_pass 末尾斜杠:行為完全不同
這是 Nginx 反向代理中被問頻率最高的問題。proxy_pass 末尾有無斜杠,轉(zhuǎn)發(fā)路徑行為完全不同。
行為對比
# 寫法 A:無斜杠
location /api/ {
proxy_pass http://backend;
}
# 寫法 B:有斜杠
location /api/ {
proxy_pass http://backend/;
}
| 客戶端請求路徑 | 寫法 A 轉(zhuǎn)發(fā)到 | 寫法 B 轉(zhuǎn)發(fā)到 |
|---|---|---|
| /api/users | /api/users(保留前綴) | /users(去掉前綴) |
| /api/v1/order | /api/v1/order | /v1/order |
選擇依據(jù)
后端路由是什么,就用哪種寫法:
# 后端路由:GET /users → 用有斜杠版本
location /api/ {
proxy_pass http://backend/;
}
# 后端路由:GET /api/users → 用無斜杠版本
location /api/ {
proxy_pass http://backend;
}
常見錯誤場景
后端路由沒有 /api/ 前綴,但 proxy_pass 用了無斜杠寫法:
# 錯誤:后端收到 /api/users,但路由只有 /users,導致 404
location /api/ {
proxy_pass http://backend; # 應(yīng)改為 http://backend/
}
三、502 Bad Gateway 三大排查路徑
遇到 502,按以下順序逐步排查,不要跳步。
路徑一:確認 upstream 服務(wù)狀態(tài)
從 Nginx 容器內(nèi)部直接發(fā)起請求,驗證網(wǎng)絡(luò)連通性和服務(wù)可用性:
# 基礎(chǔ)連通性測試 docker exec -it nginx-container curl -v http://backend:3000/health # 測試完整請求(帶 Host 頭) docker exec -it nginx-container curl -v \ -H "Host: example.com" \ http://backend:3000/api/users
如果此步驟不通,問題在網(wǎng)絡(luò)層或后端服務(wù)本身,與 Nginx 配置無關(guān)。
路徑二:檢查超時配置
proxy_connect_timeout 10s; # 與 upstream 建立 TCP 連接的超時 proxy_send_timeout 60s; # 向 upstream 發(fā)送請求的超時 proxy_read_timeout 60s; # 等待 upstream 響應(yīng)的超時(504 的主要來源)
常見場景對照:
| 場景 | 建議值 |
|---|---|
| 普通 API 接口 | proxy_read_timeout 30s |
| 含數(shù)據(jù)庫復(fù)雜查詢的接口 | proxy_read_timeout 120s |
| 文件導出/報表生成 | proxy_read_timeout 300s |
| WebSocket 長連接 | proxy_read_timeout 3600s |
路徑三:檢查重試范圍配置
# 默認值:僅在 error 和 timeout 時重試 proxy_next_upstream error timeout; # 錯誤配置示例:把后端正常業(yè)務(wù)錯誤也當節(jié)點故障處理 # proxy_next_upstream error timeout http_404 http_500; # 會導致一次用戶請求觸發(fā)多次后端調(diào)用
如果發(fā)現(xiàn)后端接口被調(diào)用了兩次,或者 POST 請求產(chǎn)生了重復(fù)數(shù)據(jù),優(yōu)先檢查 proxy_next_upstream 是否配置范圍過寬。
四、upstream keepalive 配置
Nginx 與 upstream 之間默認使用短連接,每次請求建立新的 TCP 連接。高并發(fā)場景下 TCP 握手開銷顯著,建議配置 keepalive 長連接。
完整配置
upstream backend {
server backend:3000;
keepalive 32; # 每個 worker 進程保持的最大空閑長連接數(shù)
}
server {
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1; # keepalive 要求 HTTP/1.1
proxy_set_header Connection ""; # 清除 Connection: close,啟用長連接
}
}
重要:
proxy_http_version 1.1和proxy_set_header Connection ""必須同時配置,缺少任意一行 keepalive 均不生效。這是最常見的"配了等于沒配"問題。
參數(shù)說明
keepalive 32:每個 worker 進程緩存的最大空閑連接數(shù),不是總連接上限- 實際最大空閑連接數(shù) =
keepalive×worker_processes - 建議值:并發(fā)量不高時設(shè) 16~32,高并發(fā)服務(wù)可適當增大
五、Host 頭與真實 IP 透傳
問題描述
Nginx 轉(zhuǎn)發(fā)請求時,默認將 Host 頭替換為 upstream 地址(如 backend:3000),將客戶端 IP 替換為 Nginx 容器 IP,導致后端應(yīng)用拿到錯誤的域名和 IP。
標準透傳配置
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $http_host; # 原始 Host(含端口)
proxy_set_header X-Real-IP $remote_addr; # 直連客戶端 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # IP 鏈
proxy_set_header X-Forwarded-Proto $scheme; # 原始協(xié)議 http/https
}
$http_host 與 $host 的區(qū)別:
| 變量 | 示例值 | 包含端口 |
|---|---|---|
| $http_host | example.com:8080 | ? |
| $host | example.com | ? |
CDN/SLB 多層代理場景
當 Nginx 前面還有 CDN 或負載均衡時,$remote_addr 是上層代理的 IP 而非客戶端真實 IP,需要配置 ngx_http_realip_module:
# 聲明信任的上游 IP 段(CDN/SLB 的 IP 段) set_real_ip_from 103.21.244.0/22; set_real_ip_from 103.22.200.0/22; # 從哪個請求頭中提取真實 IP real_ip_header X-Forwarded-For; # 遞歸查找,跳過信任 IP 段,找到第一個非信任 IP real_ip_recursive on;
配置后,$remote_addr 將自動替換為真實客戶端 IP。
六、文件上傳 413 完整處理
6.1 Nginx 限制配置
# 全局配置(http 塊)
http {
client_max_body_size 100m;
}
# 或僅對上傳路由生效
location /upload/ {
client_max_body_size 500m;
client_body_timeout 300s; # 客戶端上傳請求體的超時(慢速上傳場景)
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_pass http://backend;
}
6.2 后端框架同步修改
僅修改 Nginx 不夠,后端框架通常也有獨立的上傳限制:
| 框架/運行時 | 配置項 | 默認值 |
|---|---|---|
| PHP | upload_max_filesize + post_max_size(php.ini) | 2M |
| Node.js(Express) | bodyParser limit 參數(shù) | 100kb |
| Spring Boot | spring.servlet.multipart.max-file-size | 1MB |
| Nginx 本身 | client_max_body_size | 1m |
6.3 上傳 buffer 優(yōu)化
# 默認 buffer 只有 8k/16k,大文件上傳會頻繁寫臨時文件 client_body_buffer_size 1m; # 分級臨時目錄,避免單目錄文件過多影響性能 client_body_temp_path /tmp/nginx_upload 1 2;
Docker 環(huán)境注意:臨時目錄需要確保掛載了有足夠空間的卷,且 Nginx 進程有寫權(quán)限。
七、CORS 跨域完整配置
7.1 完整配置模板
location /api/ {
# OPTIONS 預(yù)檢請求單獨處理
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, PATCH, OPTIONS';
add_header Access-Control-Allow-Headers 'Authorization, Content-Type, X-Requested-With';
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Max-Age 86400;
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
# 正常請求添加 CORS 頭
add_header Access-Control-Allow-Origin $http_origin always; # always 不能漏
add_header Access-Control-Allow-Credentials true always;
proxy_pass http://backend;
}
7.2 三個常見錯誤
錯誤一:* 與 Credentials 同時使用
# 錯誤配置:瀏覽器會直接報錯拒絕 add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Credentials true;
瀏覽器規(guī)范:Allow-Credentials: true 時,Allow-Origin 不能為 *,必須是具體域名。
多域名場景的正確處理:
set $cors_origin "";
if ($http_origin ~* ^https?://(example\.com|app\.example\.com|staging\.example\.com)$) {
set $cors_origin $http_origin;
}
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials true always;
錯誤二:漏寫 always 關(guān)鍵字
add_header Access-Control-Allow-Origin $http_origin; # 錯誤 add_header Access-Control-Allow-Origin $http_origin always; # 正確
不加 always:后端返回 4xx/5xx 時,Nginx 不添加 CORS 響應(yīng)頭,瀏覽器攔截響應(yīng),前端只看到 CORS 錯誤,無法獲取真實錯誤信息。
錯誤三:未處理 OPTIONS 預(yù)檢
帶自定義請求頭(如 Authorization)的請求,瀏覽器會先發(fā) OPTIONS 預(yù)檢,Nginx 需要單獨響應(yīng) 204,否則預(yù)檢失敗,實際請求不會發(fā)出。
八、location 匹配優(yōu)先級
Nginx location 匹配不是從上到下順序匹配,而是按優(yōu)先級規(guī)則決定。
優(yōu)先級從高到低
| 寫法 | 類型 | 優(yōu)先級 | 說明 |
|---|---|---|---|
| location = /path | 精確匹配 | 1(最高) | 完全相等才匹配 |
| location ^~ /prefix | 前綴鎖定 | 2 | 匹配后跳過正則檢查 |
| location ~ /regex | 正則(區(qū)分大小寫) | 3 | |
| location ~* /regex | 正則(不區(qū)分大小寫) | 3 | |
| location /prefix | 普通前綴 | 4(最低) | 最長匹配勝出 |
典型踩坑案例
# 預(yù)期:/api/v2/files/photo.jpg 走第一個 location
location /api/v2/files/ {
proxy_pass http://file-service/;
}
# 實際:正則優(yōu)先級更高,.jpg 請求走這里
location ~* \.(jpg|png|gif|webp)$ {
expires 6M;
add_header Cache-Control "public";
}
解決:使用 ^~ 鎖定前綴,阻止正則匹配接管
location ^~ /api/v2/files/ {
proxy_pass http://file-service/;
}
include 文件加載順序
/etc/nginx/conf.d/*.conf 按文件名字母順序加載,default_server 生效的是第一個加載的 server 塊,與文件內(nèi)容書寫順序無關(guān)。
# 推薦:用數(shù)字前綴明確控制加載順序 00-default.conf 10-app-main.conf 20-app-admin.conf
九、alias 與 root 的路徑行為差異
兩個指令都用于指定靜態(tài)文件根目錄,但路徑拼接邏輯不同,搞混會導致 404。
root:location 路徑追加在 root 之后
location /static/ {
root /data;
}
# 請求 /static/img/logo.png
# 實際查找路徑:/data/static/img/logo.png
# ^^^^^ ^^^^^^^^
# root location路徑也包含在內(nèi)
alias:location 路徑替換為 alias
location /static/ {
alias /data/assets/; # 末尾斜杠必須有
}
# 請求 /static/img/logo.png
# 實際查找路徑:/data/assets/img/logo.png
# ^^^^^^^^^^^^ 只有 alias 路徑,location 路徑被替換
try_files 注意事項
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
try_files 的路徑均相對 root 拼接。如果 root 路徑在容器內(nèi)不存在(如掛載路徑寫錯),try_files 一路找不到,最終返回 403 或 500。Docker 部署時需確認 volume 掛載路徑與 root 配置一致。
十、排查命令速查
# 驗證配置語法(必須先做) docker exec nginx-container nginx -t # 優(yōu)雅重載配置 docker exec nginx-container nginx -t && \ docker exec nginx-container nginx -s reload # 查看當前已生效的完整配置(含 include 展開) docker exec nginx-container nginx -T # 實時查看錯誤日志 docker exec nginx-container tail -f /var/log/nginx/error.log # 從容器內(nèi)測試 upstream 連通性(排查 502 第一步) docker exec nginx-container curl -v http://backend:3000/health # 測試帶 Host 頭的請求 docker exec nginx-container curl -v \ -H "Host: example.com" \ http://backend:3000/api/users # 測試 CORS 預(yù)檢請求 curl -v -X OPTIONS \ -H "Origin: https://example.com" \ -H "Access-Control-Request-Method: POST" \ http://your-nginx/api/users
總結(jié)
本文覆蓋了 Nginx 反向代理配置中的 10 類常見問題:
- 改配置先驗證 → nginx -t + && 串聯(lián) reload
- proxy_pass 斜杠 → 有無斜杠決定路徑前綴是否保留
- 502 排查 → 連通性 → 超時 → 重試邏輯,按順序來
- keepalive → 必須配對 proxy_http_version 1.1 + Connection ""
- 透傳頭 → Host、X-Real-IP、X-Forwarded-For 標準寫法
- 413 文件上傳 → Nginx + 后端框架同步修改
- CORS → always 不能漏,Credentials 不能用 *
- location 優(yōu)先級 → 不是順序匹配,^~ 鎖定前綴
- alias vs root → 路徑拼接邏輯不同,alias 末尾要加斜杠
- try_files → 依賴 root 路徑,容器內(nèi)路徑要和掛載一致
系列文章:
- 第一篇:Docker Nginx 容器網(wǎng)絡(luò)與端口避坑指南
- 本篇:反向代理配置:proxy_pass、502排查、CORS、文件上傳(當前)
- 第三篇:SSL/TLS 配置、WebSocket 代理與性能調(diào)優(yōu)
- 第四篇:日志管理、權(quán)限配置、健康檢查與生產(chǎn)部署
到此這篇關(guān)于Nginx 反向代理配置避坑指南(proxy_pass 斜杠、502排查、CORS、文件上傳)的文章就介紹到這了,更多相關(guān)Nginx 反向代理配置內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Windows安裝nginx1.10.1反向代理訪問IIS網(wǎng)站
這篇文章主要為大家詳細介紹了Windows安裝nginx1.10.1反向代理訪問IIS網(wǎng)站的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-11-11

