CORS跨域检查器
跨域诊断面板
由服务端模拟预检与实际请求,快速判断是 Allow-Origin、Allow-Headers 还是 credentials 出了问题。
服务端真实模拟CORS预检与实际请求,6大响应头逐项检查,精准定位跨域配置错误,提供Nginx/Node.js/Spring等多框架修复代码。
由服务端模拟预检与实际请求,快速判断是 Allow-Origin、Allow-Headers 还是 credentials 出了问题。
服务端真实模拟CORS预检与实际请求,6大响应头逐项检查,精准定位跨域配置错误,提供Nginx/Node.js/Spring等多框架修复代码。
这是最经典的CORS问题!因为Postman和curl根本不受浏览器同源策略限制——它们是HTTP客户端,不是浏览器,不会拦截响应,也不会自动发OPTIONS预检请求。跨域错误是浏览器独有的安全机制,只有浏览器的JavaScript运行环境才会执行CORS检查。Postman能调通只能证明接口本身能返回数据,不能证明CORS配置正确。你需要用浏览器、或者本工具(服务端模拟浏览器CORS流程)来检测,才会发现问题。
99%的CORS错误是后端配置问题(或者Nginx/网关层配置问题),前端能做的非常有限。CORS的核心是服务器通过响应头「授权」浏览器允许跨域访问,前端只能设置withCredentials、设置请求头这些,无法绕过浏览器的安全限制。网上说的前端用代理(devServer proxy、Nginx反向代理把前端和接口代理到同个源)本质是「欺骗」浏览器让它以为是同源请求,不是真的解决了CORS问题,生产环境如果前后端不同源还是需要后端正确配置CORS。
因为GET通常满足简单请求条件,浏览器直接发请求;而POST如果你发的是Content-Type: application/json(90%的POST接口都是这个),就不属于简单请求了,会触发OPTIONS预检。预检请求需要服务器返回正确的CORS头,很多人只给GET/POST配了CORS头但没处理OPTIONS方法,或者OPTIONS响应没带头,就报错了。PUT/DELETE/PATCH这些方法本身就不是简单方法,必然触发预检。
开发环境有几种方案:①框架自带的代理(Vue CLI的devServer.proxy、Vite的server.proxy、Create React App的proxy):把接口请求代理到前端开发服务器同源,浏览器以为是同源请求不跨域;②关闭浏览器安全参数(比如Chrome加--disable-web-security启动参数),仅限本地临时测试,绝对不能用于正常浏览;③后端配置CORS允许localhost源(推荐,和生产环境行为一致)。生产环境必须后端正确配置CORS:配置允许的源白名单、正确设置Allow-Methods/Allow-Headers/Allow-Credentials、加Vary: Origin,或者用网关/Nginx统一处理CORS。
不行,Access-Control-Allow-Origin响应头只能有一个值,要么是具体的一个源(如https://a.com),要么是*。浏览器不接受多个源(比如写https://a.com,https://b.com是无效的,浏览器会认为不匹配)。正确的多源配置方式是:服务器维护一个允许的源白名单列表,每次收到请求时读取请求头中的Origin值,检查这个Origin是否在白名单中,如果在就把Access-Control-Allow-Origin设置为这个具体的Origin值,同时返回Vary: Origin头;如果不在就不返回CORS头或者返回错误。不要试图返回多个Allow-Origin头或者用逗号分隔多个值,浏览器都不认。
OPTIONS预检请求不需要返回任何业务逻辑和响应体,只需要返回正确的CORS响应头和200/204状态码就可以了。浏览器收到正确的CORS头就认为预检通过,不会读取OPTIONS响应的body内容。所以最佳实践是:在Nginx/网关层直接拦截OPTIONS请求,返回204 No Content和正确的CORS头,不转发到后端应用服务器,这样既减轻后端压力,也避免后端路由不支持OPTIONS导致的405错误。但要注意确保OPTIONS响应的CORS头和实际请求的CORS头保持一致。
带Cookie的跨域请求需要同时满足三个条件:①前端XMLHttpRequest.withCredentials = true或者fetch的credentials: 'include';②后端响应头Access-Control-Allow-Credentials: true;③Access-Control-Allow-Origin不能是*,必须是具体源。三个条件缺一不可。另外还要注意Cookie的属性:Cookie必须设置了SameSite=None; Secure(HTTPS环境)才能在跨域请求中携带;如果Cookie是SameSite=Lax或Strict,跨域请求不会带;Cookie的Domain属性要正确设置;还要注意第三方Cookie政策的影响(Chrome等浏览器对第三方Cookie有限制)。
CORS是放宽跨域访问限制,CSRF是跨站请求伪造攻击,两者是不同概念。正确配置CORS不会直接导致CSRF漏洞——因为CORS只是允许JS读取响应,而CSRF是攻击者诱导用户在不知情的情况下发送请求(比如img标签、自动提交表单),这些请求即使没有CORS也会发出去带上Cookie。防御CSRF需要用CSRF Token、SameSite Cookie、验证Origin/Referer等方案,不能靠禁用CORS来防CSRF。但如果你配置CORS时把Allow-Origin设为*且允许带凭证,确实会放大CSRF风险,所以带凭证场景绝对不能用*,必须严格限制白名单源。
普通的<img>、<script>、<link>标签加载跨域资源(CDN图片、JS脚本、CSS)默认不会有CORS问题,这些是「嵌入资源」不是「AJAX请求」,浏览器允许加载但JS不能读取内容。但如果你想在Canvas中操作跨域图片、或者用fetch/XHR加载这些资源并读取内容,就需要CORS,同时标签上要加crossorigin属性。跨域文件下载(a标签点击下载)默认也不需要CORS。重定向会影响CORS:预检OPTIONS请求如果返回3xx重定向,浏览器会直接拒绝(Preflight redirect is not allowed);实际请求的重定向如果是跨源的,需要每一跳都正确返回CORS头。
Nginx推荐配置要点:①用map指令匹配Origin白名单,动态设置$cors_origin变量;②OPTIONS请求直接返回204不转发后端;③add_header记得加always参数确保错误响应也带头;④加上Vary: Origin;⑤正确配置Allow-Methods/Allow-Headers/Credentials。示例配置可以在本工具检测结果的修复建议中找到,针对Nginx、Apache、Node.js Express、Spring Boot、Python Flask/Django、Koa、Go Gin等主流框架我们都提供了经过验证的配置片段。
验证CORS的几个步骤:①先用本工具在线检测,输入URL、Origin、方法、头、凭证选项,看预检和实际请求的各项检查是否通过;②打开浏览器DevTools的Network面板,勾选Disable cache禁用缓存(避免旧缓存干扰),触发跨域请求,检查OPTIONS预检和实际请求的响应头是否正确;③在Console看有没有CORS相关错误;④测试不同场景:不带自定义头的GET请求、带application/json的POST请求、PUT/DELETE方法、带Cookie/不带Cookie、带自定义头;⑤用curl模拟OPTIONS请求:curl -i -X OPTIONS -H "Origin: https://yoursource.com" https://api.example.com/endpoint,检查响应头是否正确。
不同浏览器对CORS规范的实现细节有差异:①Safari对预检缓存Max-Age限制更严格(早期版本只有600秒),Cookie策略(ITP智能反跟踪)也更严格,可能导致跨域Cookie问题;②Chrome对带凭证时的通配符检查更严格,Allow-Headers/Allow-Methods也不允许*,Firefox某些版本可能宽松些;③旧版IE(IE11)用的是XDomainRequest而不是标准XMLHttpRequest,CORS实现有很多坑(比如不支持自定义头、只支持GET/POST);④不同浏览器对Vary头的处理、重定向处理、安全头的处理有细微差异。解决方法是严格按照CORS规范配置,不要依赖某一个浏览器的兼容行为。
建议设置为3600(1小时)到86400(24小时)之间。设置太短(比如60秒):预检缓存很快过期,频繁发送OPTIONS请求,增加请求耗时和服务器压力,在移动弱网环境下影响明显。设置太长(比如31536000即一年):如果你更新了CORS配置(比如新增了允许的方法、头),用户浏览器缓存的旧预检结果可能要很久才过期,期间会出现配置不生效的问题。另外Chrome和Firefox会自动把超过86400秒的值截到86400秒,你设再长也没用。开发环境可以设置为-1禁用预检缓存,方便调试。
WebSocket(ws://和wss://)不受HTTP CORS机制限制,因为WebSocket是独立的协议,不是HTTP AJAX请求。但WebSocket握手阶段是HTTP请求,服务器可以检查Origin头来决定是否允许连接——这是WebSocket层面的权限控制,不是标准CORS,但原理类似。如果WebSocket连接被拒绝,你需要检查WebSocket服务端的Origin校验配置,而不是HTTP CORS头。Socket.IO等库可能有自己的跨域配置方式,和标准CORS配置类似但不完全一样。另外,HTTP/2 Server Push、WebRTC等其他浏览器API也有各自的权限控制,不完全遵循HTTP CORS。
本工具在服务端严格按照W3C CORS规范模拟浏览器的预检和实际请求流程,对CORS头的检查逻辑和现代浏览器基本一致。如果工具检测通过但浏览器还是报错,可能的原因:①浏览器缓存了旧的预检结果或响应,按Ctrl+F5强制刷新或者清除缓存再试;②浏览器扩展(广告拦截、隐私保护、安全插件)修改或拦截了请求;③你在工具中填写的Origin、请求头、方法、凭证选项和实际前端发送的不一致(比如前端实际还带了某个自定义头但你没在工具里加);④CDN/代理节点缓存不一致,工具请求的节点和浏览器请求的节点返回不同结果;⑤浏览器有特殊安全策略(比如HTTPS页面请求HTTP混合内容被拦截、本地文件file://协议的特殊限制)。