CORS配置踩坑指南:从原理到实战的完整解决方案
本地开发调接口又报 CORS 错了,这次真不是后端的锅
今天本地跑一个新项目,前端用 Vite,后端 API 部署在测试环境。一调接口就报错:
Access to fetch at ‘https://jztheme.com/api/user’ from origin ‘http://localhost:5173’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
我第一反应是:“后端又没配 CORS?”,赶紧去群里@后端同事。结果人家回我:“线上和测试环境都配了,你本地自己代理一下不就完了?”
行吧,确实是我的问题。但之前一直用 Create React App,自带 proxy 配置,现在换 Vite 了,得重新折腾。结果这一折腾,踩了好几个坑。
先试了最简单的 proxy,结果根本没生效
Vite 的文档里说,开发环境下可以用 server.proxy 配置代理。我照着写了个:
// vite.config.js
export default defineConfig({
server: {
proxy: {
'/api': 'https://jztheme.com'
}
}
})
然后前端代码里改成:
fetch('/api/user')
结果控制台还是报 CORS 错!而且请求地址直接变成了 http://localhost:5173/api/user,根本没转发到 jztheme.com。我一度以为 Vite 的 proxy 是摆设。
后来翻 issue 才发现:**Vite 的 proxy 只在开发服务器(dev server)里生效,必须通过 Vite 启动的本地服务访问,不能直接打开 HTML 文件**。我那天因为赶时间,临时用 Live Server 打开了 index.html,绕过了 Vite dev server,proxy 当然不工作。切回 npm run dev 就好了——这坑纯属自己手快没注意环境。
但改完 proxy 还有个小问题:cookie 带不上
接口需要登录态,靠 cookie 传 sessionId。改完 proxy 后,虽然能通了,但后端一直说“未登录”。抓包一看,请求压根没带 cookie。
查了下,Vite 的 proxy 默认不会透传 credentials(比如 cookie)。得手动开启:
// vite.config.js
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'https://jztheme.com',
changeOrigin: true,
secure: false, // 测试环境用自签名证书时加这个
cookieDomainRewrite: 'localhost', // 关键!重写 cookie domain
configure: (proxy, options) => {
// 如果需要更细控制,可以在这里操作
}
}
}
}
})
这里 cookieDomainRewrite 是关键。因为后端返回的 cookie domain 是 .jztheme.com,浏览器在 localhost 下根本不会存。重写成 localhost 后,cookie 才能被保存并在后续请求中带上。
不过要注意,这个配置只对开发环境有效。生产环境部署时,前端和后端必须同域,或者后端显式设置 Access-Control-Allow-Credentials: true 并指定 Access-Control-Allow-Origin 为具体域名(不能是 *)。
后端其实也得配合,但很多人忽略了这点
虽然本地开发用 proxy 能绕过 CORS,但上线后如果前后端分离部署,CORS 还是得靠后端配。我让后端同事检查了他们的 Nginx 配置,发现他们只加了:
add_header Access-Control-Allow-Origin *;
这在不需要 credentials(比如 cookie)时没问题。但一旦涉及登录态,就必须改成具体域名,比如:
location /api/ {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin 'https://your-frontend.com';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
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-Allow-Credentials 'true';
add_header Access-Control-Max-Age 1728000;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
add_header Access-Control-Allow-Origin 'https://your-frontend.com' always;
add_header Access-Control-Allow-Credentials 'true' always;
}
注意两点:
Access-Control-Allow-Origin不能是*,必须写死前端域名Access-Control-Allow-Credentials: true必须和具体的 origin 一起出现,否则浏览器会拒绝
后端同事一开始死活不信,说“我们线上好好的”。后来发现他们线上是同域部署(前端打包进后端 static 目录),所以根本没触发 CORS。测试环境是分开部署的,才暴露问题。这属于典型的“线上侥幸,线下踩坑”。
附:完整的 Vite 代理配置(亲测有效)
最后贴一个我当前项目里用的配置,支持带 cookie 的 API 调用:
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
server: {
port: 5173,
proxy: {
'/api': {
target: 'https://jztheme.com',
changeOrigin: true,
secure: false, // 如果测试环境用 HTTPS 但证书无效
cookieDomainRewrite: 'localhost',
rewrite: (path) => path.replace(/^/api/, '')
}
}
}
})
前端调用时直接写 /api/xxx,Vite 会自动转发到 https://jztheme.com/xxx,并处理 cookie 和 headers。
补充一点:如果后端接口路径不是以 /api 开头,比如是 /v1/user,那 proxy key 也要对应改成 /v1,或者用正则匹配。不过一般团队都会约定 API 前缀,避免冲突。
总结:CORS 问题,八成是环境没配对
这次折腾下来,我发现 CORS 问题通常不是“技术难点”,而是“环境认知偏差”:
- 本地开发:用 dev server 的 proxy,别手贱用 Live Server
- 测试/生产:前后端必须明确部署方式(同域 or 跨域),跨域时后端 CORS 配置必须完整
- 带 cookie 的请求:前端要开 credentials,后端要指定具体 origin + 允许 credentials
改完这些,基本就没问题了。虽然还有一点小瑕疵——比如某些老接口没按规范走 /api 前缀,得单独配 proxy 规则,但无伤大雅,能跑就行。
以上是我踩坑后的总结,如果你有更好的方案欢迎评论区交流。比如有没有办法在 Vite 里动态读取 .env 里的 API 地址来配置 proxy?我试过但没成功,可能得写个插件……

暂无评论