Nginx反向代理解決跨域問題詳解
核心原理
Nginx反向代理解決跨域的核心思路是讓客戶端請求同域名下的接口,由Nginx將請求轉發到目標服務器,從而規避瀏覽器的同源策略限制。
客戶端(同源:www.domain.com)↓Nginx(同源:www.domain.com)↓
目標服務器(跨域:api.external.com)
完整配置與代碼示例
基本反向代理配置
# /etc/nginx/conf.d/default.confserver {listen 80;server_name mydomain.com; # 前端域名# 前端靜態資源location / {root /usr/share/nginx/html;index index.html;}# 接口代理配置location /api {# 后端實際地址(跨域的目標服務器)proxy_pass http://api.external.com;# 設置必要的請求頭proxy_set_header Host $proxy_host; # 保留原始Hostproxy_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;# CORS支持(可選)add_header 'Access-Control-Allow-Origin' '*' always;add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always;add_header 'Access-Control-Allow-Credentials' 'true' always;# 處理OPTIONS預檢請求if ($request_method = 'OPTIONS') {add_header 'Access-Control-Max-Age' 1728000;add_header 'Content-Type' 'text/plain; charset=utf-8';add_header 'Content-Length' 0;return 204;}}
}
關鍵配置解析
-
proxy_pass:
location /api {proxy_pass http://api.external.com; }
- 所有以
/api
開頭的請求都會被轉發到http://api.external.com
- 所有以
-
請求頭保留:
proxy_set_header Host $proxy_host; # 保留原始主機頭 proxy_set_header X-Real-IP $remote_addr; # 保留客戶端真實IP
-
CORS支持:
add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
- 直接從Nginx層支持CORS,后端服務不需要額外處理
-
OPTIONS請求處理:
if ($request_method = 'OPTIONS') {add_header 'Access-Control-Max-Age' 1728000;# ...return 204; }
- 直接響應預檢請求,減輕后端服務器壓力
常見場景定制配置
場景1:URL重寫(刪除API前綴)
location /api {# 重寫路徑:移除/api前綴rewrite ^/api/(.*)$ /$1 break;proxy_pass http://api.external.com;
}
場景2:添加API前綴
location /user-service {# 添加/api前綴rewrite ^/user-service/(.*)$ /api/$1 break;proxy_pass http://api.external.com;
}
場景3:負載均衡
upstream backend {server backend1.example.com:8080 weight=3; # 權重server backend2.example.com:8081;server backup.example.com:8082 backup; # 備用服務器
}location /api {proxy_pass http://backend; # 使用負載均衡器proxy_set_header Host $host;
}
場景4:WebSocket支持
location /socket {proxy_pass http://ws-server.com;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";proxy_set_header Host $host;
}
前端代碼示例
// 請求代理路徑(不再需要直接訪問跨域地址)
const API_BASE = '/api'; // 與Nginx配置中的location匹配// GET請求示例
async function getData() {try {const response = await fetch(`${API_BASE}/data`, {method: 'GET',headers: {'Content-Type': 'application/json'}});return await response.json();} catch (error) {console.error('Error fetching data:', error);}
}// POST請求示例
async function postData(data) {try {const response = await fetch(`${API_BASE}/save`, {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': `Bearer ${token}`},body: JSON.stringify(data)});return await response.json();} catch (error) {console.error('Error posting data:', error);}
}
完整操作流程
-
安裝Nginx:
# Ubuntu/Debian sudo apt update sudo apt install nginx# CentOS/RHEL sudo yum install epel-release sudo yum install nginx
-
編輯配置文件:
sudo nano /etc/nginx/conf.d/default.conf
添加上面的代理配置
-
測試配置:
sudo nginx -t
輸出
syntax is ok
和test is successful
表示配置正確 -
重啟Nginx:
sudo systemctl restart nginx # 或 sudo service nginx restart
-
部署前端項目:
# 將前端構建文件放入指定目錄 sudo cp -R /path/to/dist /usr/share/nginx/html
-
驗證訪問:
訪問http://your-domain.com
應該加載前端頁面,所有API請求自動代理到目標服務
優勢對比
方案 | 是否需要修改代碼 | 安全性 | 性能影響 | 多服務支持 |
---|---|---|---|---|
Nginx反向代理 | ? | ????? | 幾乎沒有 | ????? |
CORS | ? | ???? | 中等 | ?? |
JSONP | ? | ? | 高 | ? |
Webpack代理 | ? (僅開發) | ?? | 低(開發) | ?? |
注意事項
-
路徑匹配:
- 確保
location
指令的路徑匹配模式與前端請求一致 - 使用正則表達式處理復雜的URL模式
- 確保
-
日志調試:
location /api {proxy_pass http://api.external.com;access_log /var/log/nginx/api-access.log;error_log /var/log/nginx/api-error.log; }
-
超時設置:
proxy_connect_timeout 60s; # 連接超時 proxy_send_timeout 60s; # 發送超時 proxy_read_timeout 180s; # 讀取超時
-
Cookie傳遞:
proxy_cookie_domain api.external.com mydomain.com; proxy_cookie_path / /api/;
-
安全限制:
- 避免完全開放的跨域(
Access-Control-Allow-Origin: *
) - 建議使用具體的域名白名單
- 避免完全開放的跨域(
Nginx反向代理是當前解決跨域問題最成熟、穩定、高性能的解決方案,特別適合生產環境使用,既能解決跨域問題,又能實現負載均衡、安全防護等額外好處。