Nginx是本地开发前后端分离项目时最稳妥的跨域代理方案,通过反向代理使前后端同源(如均走http://localhost),天然规避CORS限制,且复用线上配置、不依赖后端改码或浏览器插件。
本地开发时用
localhost
调前端、后端分离项目,浏览器报
CORS 错误
是常态。Nginx 是最稳妥的
跨域
代理方案——它不依赖后端改代码,也不靠浏览器插件“作弊”,适合真实还原生产环境的请求链路。
为什么推荐 Nginx 代理而不是其他方式
前端直接调
→
,本质是两个不同端口,浏览器视为跨域。常见替代方案有:
前端用
(如 vite.config.ts 中的
):只对开发服务器生效,无法覆盖所有请求(比如 iframe、fetch with credentials),且上线即失效;
后端加 CORS 头(
):开发环境可快速验证,但涉及凭证(cookie/token)时需配
,而
不兼容,必须写死 origin,本地多端口难维护;
禁用浏览器安全策略(如 Chrome 启动参数):危险、不可持续、团队协作不现实。
Nginx 在中间层做反向代理,让浏览器认为前后端同源(例如都走
),天然绕过跨域检查,还能复用线上配置逻辑。
基础 Nginx 配置:把 /api 代理到后端服务
假设后端运行在
,前端静态资源放在
,希望访问
加载页面,
转发给后端:
}
Nginx
在宝塔面板中轻松管理Nginx高性能Web服务器。提供可视化配置反向代理、负载均衡、SSL证书及HTTP缓存功能,一键优化高并发性能,助您高效搭建稳定、快速的网站运行环境。
下载
注意:
结尾的
很关键——它会剥离
前缀再转发。例如请求
,实际发给后端的是
。若后端接口本就带
前缀,此处应去掉
末尾的
,并用
控制路径。
处理带 Cookie 的跨域请求(登录态保持)
如果前端调登录接口后需携带 cookie 访问后续 API,光代理不够,还要显式开启凭证支持:
前端 fetch 必须加
;
Nginx 配置中增加:
(仅 HTTPS 环境需
,本地 HTTP 可省略);
后端响应头需含:
和明确的
(不能为
),例如
;
Nginx 可补全缺失的 CORS 头(如果后端没配):
⚠️ 注意:预检请求(OPTIONS)需被正确响应。Nginx 可直接返回 204:
调试技巧与常见坑
Nginx 配置修改后必须 reload(),不是 restart;
确认前端请求地址已改为
(而非
),否则仍跨域;
用浏览器开发者工具的 Network 面板看请求的
Protocol
列:显示
或
说明走的是 Nginx;若显示
或直接连后端端口,说明代理未生效;
遇到 502 Bad Gateway:先
确认后端是否真在运行;再检查
地址是否可通(Nginx 运行用户是否有权限访问该地址);
Mac 用户若用 Homebrew 安装 Nginx,默认配置在
,别改错文件。
不复杂但容易忽略。
http://localhost:3000http://localhost:8000/apiproxyserver.proxyAccess-Control-Allow-Origin: *credentials: true*http://localhost/http://127.0.0.1:8000/path/to/disthttp://localhost/http://localhost/api/xxxserver {
listen 80;
server_name localhost;
# 前端静态资源
location / {
root /path/to/dist;
try_files $uri $uri/ /index.html;
}
# API 接口代理
location /api/ {
proxy_pass http://127.0.0.1:8000/;
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;
}proxy_pass//api//api/users/users/apiproxy_pass/rewritecredentials: 'include'proxy_cookie_path / "/; SameSite=None; Secure";SecureAccess-Control-Allow-Credentials: trueAccess-Control-Allow-Origin*http://localhostadd_header 'Access-Control-Allow-Origin' 'http://localhost';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';location /api/ {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'http://localhost';
add_header Access-Control-Allow-Credentials 'true';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS, PUT, DELETE';
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header Access-Control-Max-Age 86400;
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
proxy_pass http://127.0.0.1:8000/;
# ... 其他 proxy_* 配置
}nginx -s reload/api/xxxhttp://localhost:8000/api/xxxhttp/1.1h2wscurl http://127.0.0.1:8000/healthproxy_pass/opt/homebrew/etc/nginx/nginx.conf