cors跨域问题本质是浏览器拦截预检请求,需后端显式配置access-control-allow-origin等响应头,前端无法绕过;nginx或spring boot需正确处理options预检,且带credentials时不能用通配符;暴露自定义响应头须设access-control-expose-headers;开发代理仅限本地,上线须切换真实api地址。

后端没配 Access-Control-Allow-Origin,前端发请求直接被拦
浏览器在跨域时会先发一个 OPTIONS 预检请求,如果响应头里没有正确的 CORS 头,后续的 GET 或 POST 就根本不会发出——你连网络面板都看不到主请求,只看到 OPTIONS 返回 403 或 500。
常见错误现象:Failed to fetch、No 'Access-Control-Allow-Origin' header、控制台报错但后端日志里压根没收到主请求。
- 必须由后端在响应头中显式设置
Access-Control-Allow-Origin,前端加mode: 'cors'或credentials: 'include'不会绕过限制 - 若前端带 cookie(
credentials: 'include'),后端不能用通配符*,必须写明确域名,比如https://myapp.com - 如果用 Nginx 做反向代理,可以在 Nginx 层加头,但要注意:预检请求可能不带
Origin,需用if ($http_origin) { ... }判断再添加 CORS 头
Spring Boot 2.4+ 默认不放行 OPTIONS,@CrossOrigin 失效
新版本 Spring Boot 把 CORS 预检逻辑收得更紧,仅靠 @CrossOrigin 注解无法覆盖全局 OPTIONS 路径,尤其当接口用了自定义路径前缀或有拦截器时,预检请求可能直接 405。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 优先用配置类方式注册
CorsConfigurationSource,而不是只依赖注解 - 确保
allowedOrigins包含前端实际地址(开发环境常用http://localhost:3000),不要漏掉协议和端口 - 如果用了 Spring Security,必须在安全配置里显式调用
http.cors(),否则安全链会拦截预检请求 - 示例关键代码:
@Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(Arrays.asList("http://localhost:3000")); config.setAllowCredentials(true); config.addAllowedMethod("GET"); config.addAllowedMethod("POST"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; }
前端 fetch 带 credentials: 'include' 却拿不到响应头
即使后端正确返回了 Access-Control-Allow-Origin 和 Access-Control-Expose-Headers,前端依然读不到自定义响应头(比如 X-Request-ID),是因为默认只暴露基础字段(Cache-Control、Content-Language 等)。
原因很直接:浏览器策略限制。不显式声明,就不可读。
- 后端必须额外设置
Access-Control-Expose-Headers,把要暴露的字段列全,多个用逗号分隔,例如:X-Request-ID, X-RateLimit-Remaining - 前端 fetch 中即使写了
credentials: 'include',也不影响响应头暴露范围;暴露与否完全取决于后端这个响应头 - 注意大小写:HTTP 头名是大小写不敏感的,但
expose列表里写成x-request-id也能生效,不过统一用 PascalCase 更稳妥
开发环境用代理绕过 CORS,上线后忘了关导致请求发错地址
Vue CLI、Vite、Create React App 都支持 proxy 配置,本地跑起来没问题,但打包后这个配置彻底失效——它只是开发服务器的功能,不是运行时逻辑。
典型翻车场景:开发时所有 API 走 /api/xxx,代理到 http://localhost:8080;上线后没改请求地址,结果前端拼命往自己域名下发 /api/xxx,404 一地。
- 代理配置只在
devServer或vite.config.ts的server.proxy里生效,构建产物里不存在 - 上线前务必检查请求 base URL:开发用相对路径或变量注入,别硬编码
/api后又依赖代理 - 推荐做法:用环境变量区分 baseURL,例如
VITE_API_BASE_URL=https://api.example.com,代码里统一读取,避免混用代理和真实地址











