CORS配置踩坑指南:从原理到实战的完整解决方案

爱学习的明月 前端 阅读 2,193
赞 11 收藏
二维码
手机扫码查看
反馈

本地开发调接口又报 CORS 错了,这次真不是后端的锅

今天本地跑一个新项目,前端用 Vite,后端 API 部署在测试环境。一调接口就报错:

CORS配置踩坑指南:从原理到实战的完整解决方案

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?我试过但没成功,可能得写个插件……

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论