1. 一句话理解 CORS
CORS,全称 Cross-Origin Resource Sharing,中文常叫 跨源资源共享。
它不是“网络不通”,也不是“接口坏了”,而是:
浏览器出于安全考虑,不允许一个网页随便读取另一个源的数据。如果服务端愿意被某个前端页面读取,就必须通过一组 HTTP Header 明确告诉浏览器:“我允许这个来源访问我。”
所以跨域问题的核心不是前端,也不是后端单独的问题,而是:
浏览器、前端请求、服务端响应 Header 三者之间的协商失败。
2. 什么叫“同源”?
一个 URL 的源由三部分组成:
例如:
它的源是:
下面这些都不是同源:
端口不同,跨域。
协议不同,跨域。
主机不同,跨域。即使它们可能指向同一台机器,浏览器也认为不同源。
子域名不同,跨域。
路径不影响同源判断:
它们同源,因为协议、域名、端口相同。
3. 为什么浏览器要限制跨域?
假设没有同源策略,会出现严重安全问题。
比如你已经登录了银行网站:
然后你打开了一个恶意网站:
如果浏览器允许
evil.com 的 JavaScript 随便请求并读取 bank.com 的数据,那么恶意网站就可能读取你的账户信息。所以浏览器默认限制:
A 网站里的 JavaScript 不能随便读取 B 网站的响应内容。
注意,这里重点是 读取响应内容。
有些跨源请求本身可能会发出去,比如图片、表单、script 标签等。但 JavaScript 想读取另一个源的接口响应时,就会受到限制。
4. CORS 解决的是什么问题?
CORS 不是关闭安全限制,而是提供一个标准流程,让服务端可以明确授权。
例如前端页面在:
后端 API 在:
浏览器会在请求里带上:
服务端如果允许它访问,就应该返回:
浏览器看到服务端明确允许当前 Origin,就把响应交给前端 JavaScript。
如果服务端没返回,或者返回的不匹配,浏览器就会拦截响应,并在控制台报 CORS 错误。
5. 最重要的几个 Header
5.1 Origin
这是浏览器自动加的请求头。
它表示:这个请求是从哪里发起的。
一般情况下,前端不需要也不能随便手动设置它。
5.2 Access-Control-Allow-Origin
这是服务端返回的响应头。
表示允许哪个源访问当前响应。
如果接口是公开资源,也可以:
但是注意:
只要涉及 Cookie、登录态、Authorization 凭证,就不要用*。
5.3 Access-Control-Allow-Methods
预检请求时,服务端告诉浏览器允许哪些 HTTP 方法:
如果前端真实请求是
PUT,但这里没有 PUT,浏览器会拦截。5.4 Access-Control-Allow-Headers
预检请求时,服务端告诉浏览器允许哪些自定义请求头:
如果前端请求带了:
那么服务端预检响应里必须允许
Authorization。5.5 Access-Control-Allow-Credentials
如果要允许跨域携带 Cookie、HTTP 认证信息、客户端证书等凭证,需要服务端返回:
前端也要配合:
Axios 中通常是:
注意:
这是错误组合。
带凭证时必须返回具体 Origin:
5.6 Access-Control-Expose-Headers
默认情况下,前端不能读取所有响应头。
如果你想让前端读取某些自定义响应头,比如:
服务端需要加:
否则请求虽然成功了,但前端通过 JS 读不到这些 Header。
5.7 Access-Control-Max-Age
预检请求可以缓存。
表示这类预检结果可以缓存一段时间,减少频繁发
OPTIONS 请求。6. 简单请求和预检请求
CORS 请求大致分两类:
7. 什么是简单请求?
简单请求不会先发
OPTIONS 预检。常见条件包括:
方法是:
并且请求头比较简单。
例如:
浏览器可能直接发真实请求。
但服务端仍然要在响应里返回:
否则浏览器还是会拦截响应。
8. 什么是预检请求?
当请求比较“危险”或“复杂”时,浏览器会先自动发一个
OPTIONS 请求,问服务端:我等下想发一个真正的请求,方法是 PUT,还带 Authorization 请求头,你允许吗?
例如前端请求:
浏览器可能先发:
服务端应该返回:
浏览器确认允许后,才会继续发真实的
PUT 请求。9. 为什么 Postman、curl 能成功,浏览器却失败?
因为 CORS 是浏览器的安全策略。
下面这些一般不受 CORS 限制:
受 CORS 限制的是:
所以你看到:
这并不矛盾。
说明网络和接口可能是通的,但服务端没有给浏览器需要的 CORS 响应头。
10. 最常见的跨域错误
10.1 后端没有返回 Access-Control-Allow-Origin
报错类似:
原因:
解决:
10.2 允许的 Origin 不匹配
前端来源是:
后端返回:
浏览器会认为不匹配。
解决:
或者后端根据请求里的
Origin 动态判断并回显。10.3 带 Cookie 时用了通配符
错误配置:
解决:
前端:
10.4 OPTIONS 请求没处理
浏览器先发了
OPTIONS,但后端没有处理,返回 404、405、500。解决:
后端必须正确响应
OPTIONS 请求。一般返回 200 或 204 即可。
10.5 没允许 Authorization
前端请求:
但后端预检响应没有:
解决:
10.6 网关或 Nginx 没放行
实际项目里 CORS 可能不是业务服务直接处理,而是在:
只要链路上有一层吞掉了
OPTIONS 或响应头,就会出问题。排查时不要只看业务代码,要看完整链路。
11. 本地开发为什么经常遇到跨域?
因为前后端开发端口通常不同:
端口不同就是跨域。
常见解决方案有两种。
12. 方案一:后端配置 CORS
适合真实环境、联调环境、生产环境。
核心思想:
谁提供 API,谁负责告诉浏览器允许哪些前端访问。
例如允许:
不建议生产环境无脑允许:
尤其是带登录态的接口。
13. 方案二:前端开发代理
适合本地开发。
例如前端请求:
开发服务器代理到:
浏览器看到的仍然是:
因为浏览器认为它请求的是同源地址,所以不会触发跨域。
Vite 示例:
前端代码:
不要写成:
否则还是跨域。
14. Nginx 示例
简单公开 API:
带 Cookie 的 API:
如果需要多个 Origin,不建议直接写多个
Access-Control-Allow-Origin。正确做法是:
15. Express 示例
16. Go 示例
17. Spring Boot 思路
可以在 Controller、全局配置、网关层配置 CORS。
核心仍然是这几件事:
不要只在单个接口上加,结果登录接口好了,上传接口又坏了。
最好统一在全局中间件或网关层处理。
18. Cookie、登录态和 CORS
跨域登录最容易踩坑。
你需要同时满足:
服务端响应:
前端请求:
Cookie 自身也要正确设置。
如果是跨站 Cookie,通常需要:
注意:
这两个问题经常混在一起,但不是同一个东西。
19. CORS 和 CSRF 的区别
CORS 主要限制:
CSRF 主要问题是:
CORS 不能完全防 CSRF。
如果接口依赖 Cookie 登录,还应该考虑:
不要以为配了 CORS 就等于安全。
20. CORS 和反向代理的区别
CORS 是浏览器安全机制。
反向代理是一种架构方案。
例如:
浏览器看到的只有:
所以前端页面和 API 是同源的,自然不需要 CORS。
这就是为什么生产环境常见做法是:
21. 看到 CORS 报错时怎么排查?
按这个顺序查。
第一步:看请求 URL
前端页面地址是什么?
接口地址是什么?
协议、域名、端口是否完全一致?
第二步:看 Network 里有没有 OPTIONS
如果有
OPTIONS,说明触发了预检。点开
OPTIONS 请求,看响应状态码。正常应该是:
如果是:
优先处理这个问题。
第三步:看 OPTIONS 响应头
至少应该有:
如果真实请求带 Cookie,还需要:
第四步:看真实请求响应头
很多人只给
OPTIONS 加了 CORS Header,但真实请求响应没有加。注意:
否则仍然失败。
第五步:看是否带 Credentials
如果前端用了:
或者 Axios:
那么后端不能返回:
必须返回具体 Origin。
第六步:看是否有重定向
预检或真实请求如果被重定向到登录页、HTTPS、其他域名,可能导致 CORS 失败。
比如:
浏览器最后看到的响应可能不是你以为的 API 响应。
第七步:看网关、CDN、Nginx
确认 CORS Header 有没有被覆盖、删除、重复添加。
特别注意:
22. 推荐的生产配置原则
22.1 不要无脑允许所有来源
不推荐:
尤其是后台管理系统、用户接口、支付接口、私有 API。
22.2 使用白名单
推荐:
后端读取请求的
Origin,判断是否在白名单中。如果在,就回显:
22.3 带 Cookie 时显式配置
同时前端:
并保证 Cookie 的
SameSite、Secure 设置正确。22.4 OPTIONS 请求要优先处理
中间件、网关、Nginx 应该能直接响应
OPTIONS。不要让预检请求进入复杂鉴权逻辑。
常见问题:
22.5 错误响应也要加 CORS Header
如果接口报 401、403、500,但响应里没有 CORS Header,前端看到的可能只是 CORS 错误,而不是实际错误。
所以 CORS Header 应尽量在统一中间件或网关层加,覆盖正常和异常响应。
23. 常见误区
误区一:前端加 Header 就能解决 CORS
错误。
CORS 主要靠服务端响应 Header 决定。
前端不能自己说“允许我跨域”。
误区二:后端接口能访问,所以不是后端问题
不一定。
Postman 能访问,只能说明接口可用。
浏览器能否访问,还要看服务端是否返回正确 CORS Header。
误区三:localhost 和 127.0.0.1 一样
对操作系统来说可能指向同一台机器。
对浏览器同源策略来说,它们不是一个源。
误区四:只要加 Access-Control-Allow-Origin: * 就行
不一定。
公开接口可以考虑。
带登录态、Cookie、后台管理接口不应该这么配。
误区五:CORS 是为了保护服务端
更准确地说,CORS 是浏览器保护用户数据的机制。
服务端仍然必须做:
不能把安全寄托在 CORS 上。
24. 一套万能排查模板
以后遇到 CORS,可以按这个模板问自己:
25. 最小正确心智模型
记住这句话:
跨域不是请求发不出去,而是浏览器不把响应交给你的 JavaScript。
再记住这张流程:
26. 最佳实践总结
开发环境:
测试环境:
生产环境:
27. 最终记忆版
CORS 就看四件事:
带登录态再加两件事:
只要你把这几个点看懂,绝大多数跨域问题都能自己解决。