聊聊跨域问题
跨域到底该谁管浏览器、代理与 Gateway CORS适用WeekFlow 前后端分离开发Vite Gateway Nginx读者前后端开发、运维1. 从一个报错说起前端跑在http://localhost:5173Gateway 在http://localhost:8080控制台出现Access to fetch at http://localhost:8080/api/v1/auth/health from origin http://localhost:5173 has been blocked by CORS policy后端说Gateway 加个CorsConfig。前端说Vite 配个 proxy 不就行了两个人都没错说的不是同一件事。把机制理顺本地能跑、上线不踩坑、排查能快。2. 跨域是浏览器的规矩CORSCross-Origin Resource Sharing不是 Spring、不是 Nginx 发明的业务规则是浏览器对页面里 JavaScript 的安全限制。浏览器在把响应交给 JS 之前会问页面来源Origin和请求 URL 是否同源不同源的话响应头里有没有「允许这个来源访问」的声明同源要求协议、域名、端口三者完全一致页面 Origin请求 URL是否跨域http://localhost:5173http://localhost:8080/api/...是端口不同http://localhost:5173http://127.0.0.1:8080/api/...是域名不同https://weekflow.comhttps://weekflow.com/api/...否同源https://app.weekflow.comhttps://api.weekflow.com/...是子域不同Postman、curl、Feign、Gateway 转发到下游——都不走这套。所以「Postman 能通、浏览器报 CORS」是正常现象。允许跨域时服务端需在响应里带上类似头Access-Control-Allow-Origin: http://localhost:5173 Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Authorization, Content-Type Access-Control-Allow-Credentials: true没有这些头浏览器会拦截JS 读不到 body。Network 里可能已是 200但fetch仍会进catch。结论限制来自浏览器「允不允许跨」必须由某层 HTTP 服务通过响应头声明。3. 前端写死后端地址一定跨域吗必须要 CorsConfig 吗3.1 写死地址 ≠ 一定跨域跨域看的是页面 Origin 和请求 URL 是否同源跟 URL 是相对路径还是写死的绝对地址无关。// 页面在 http://localhost:5173// 情况 A写死 8080 → 跨域5173 ≠ 8080fetch(http://localhost:8080/api/v1/auth/health)// 情况 B相对路径 Vite 代理 → 浏览器认为请求发往 5173同源fetch(/api/v1/auth/health)// 情况 C写死地址但页面本身就在 8080 上不常见// 例如静态资源也由 Gateway 或 Nginx 同端口提供 → 不跨域fetch(http://localhost:8080/api/v1/auth/health)环境变量也一样// .env: VITE_API_BASEhttp://localhost:8080fetch(${import.meta.env.VITE_API_BASE}/api/v1/auth/health)只要浏览器当前页面的 Origin 是5173请求目标是8080就是跨域。写死只是更容易把 host:port 写错成另一个源用相对路径/api/...则天然跟页面同源。3.2 跨域了是否必须要 CorsConfig不必须。CorsConfig只是「让 Gateway 在响应里加 CORS 头」这一种做法。跨域时浏览器要能正常读响应至少需要下面之一方案做法是否需要 Gateway CorsConfig同源代理推荐Vite / Nginx 反代前端用相对路径不需要Gateway CORSSpringCorsWebFilter需要或等价 Java/YAML 配置Nginx 层 CORSadd_header Access-Control-*不需要 Gateway 配Nginx 配不处理直连跨域 URL无任何 CORS 头浏览器拦截 JS 读响应WeekFlow 若采用开发 Vite 代理 生产 Nginx 同源反代前后端约定用/api/...则Gateway 的CorsConfig可以没有不影响正常用户访问。CorsConfig适合这些场景再开API 独立域名如api.weekflow.com页面在app.weekflow.com多个前端域名共用一个 API本地有人习惯直连http://localhost:8080调试且前端仍从 5173 发起请求第三方浏览器页面调用开放 APICORS 头只需在一层加Gateway 或 Nginx 二选一多层重复可能出 duplicate header浏览器照样报错。3.3 跨域但没 CORS 时后台有没有收到请求有可能收到了。CORS 拦的是浏览器是否允许 JS 读响应不是网络层能不能连上。典型现象Network 显示 200控制台报 CORSfetch拿不到数据。所以「接口在 Postman 里是好的」不能证明浏览器场景没问题。4. 两条解决思路思路一同源代理WeekFlow 推荐不让浏览器跨域把转发放在服务器侧。本地 — Vite// vite.config.tsexportdefaultdefineConfig({server:{proxy:{/api:{target:http://localhost:8080,changeOrigin:true,},/ws:{target:ws://localhost:8080,ws:true,changeOrigin:true,},},},})// 前端统一相对路径awaitfetch(/api/v1/auth/health)生产 — Nginxserver { listen 443 ssl http2; server_name weekflow.com; location / { root /var/www/weekflow; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://weekflow-gateway:8080; 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; } location /ws/ { proxy_pass http://weekflow-gateway:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; } }页面与 API 在浏览器里都是https://weekflow.com不触发 CORS。思路二显式 CORS浏览器直连 API 域名由 Gateway或 Nginx返回Access-Control-Allow-*。WeekFlow Gateway 当前实现weekflow-gateway模块com.weekflow.gateway.config.CorsConfig允许http://localhost:5173。若团队确定永远走同源代理该类可删除或注释并在团队规范里写清楚。5. 容易踩坑的点OPTIONS 预检带Authorization、非简单 Content-Type 时浏览器会先发 OPTIONS。直连跨域 API 时 Gateway 必须正确处理走同源代理则通常无感。WebSocketREST 代理了/ws/也要单独配Vitews: true、Nginx Upgrade 头否则表现为 HTTP 正常、长连接失败。CredentialsCookie 方案下Allow-Origin不能为*须指定具体域名且Allow-Credentials: true。Bearer Token 一般简单些。开发与生产策略不一致开发用 proxy、预发却改成https://api.staging.xxx.com直连联调阶段会突然冒出 CORS。团队应统一各环境前端 baseURL 怎么配、是否允许写死后端 host。微服务间调用Gateway → auth-service、OpenFeign 等不涉及 CORS仅影响浏览器里的 JS。6. WeekFlow 团队约定建议前端请求使用相对路径/api/...、/ws/...环境变量里不要写死后端host:port除非明确走跨域 CORS 方案。本地Vite proxy → Gateway8080。生产Nginx 同域名反代 → Gateway。GatewayCorsConfig按需启用API 独立域名或多前端源时再开。CORS 只在一层配置避免 Gateway 与 Nginx 重复。7. 排查清单浏览器报 CORS 时依次确认当前页面 Origin 是什么地址栏 端口请求完整 URL 是什么相对路径还是写死的绝对地址中间有没有 Vite / Nginx 代理代理规则是否覆盖该路径若是跨域直连Gateway 或 Nginx 是否返回了 CORS 头看 Response Headers是否只有浏览器失败、Postman 正常是则基本可断定 CORS 问题8. 相关代码与文档项位置Gateway CORS可选weekflow-gateway/.../config/CorsConfig.javaGateway 路由weekflow-gateway/src/main/resources/application.yml开发计划 Step 1.3.4docs/architecture/03-开发计划.md9. 一句话总结写死后端地址只有页面 Origin 与目标 URL 不同源时才跨域写死8080而页面在5173就会跨域。CorsConfig不是必选项是「跨域直连 API」时的一种解法同源代理方案下可以不要。团队真正要对齐的是前端 URL 怎么写、代理在哪一层、什么场景才开 CORS。