真实面经题目 · 原创解析
浏览器跨域问题如何解决?
跨域问题的本质不是浏览器完全禁止请求发出去,而是同源策略限制脚本读取不同源响应。标准解法优先是服务端正确配置 CORS;工程上还会结合同源反向代理、本地开发代理、JSONP、postMessage、WebSocket 等方案,但它们适用场景和安全边界不同。
真实面经题目 · 原创解析
跨域问题的本质不是浏览器完全禁止请求发出去,而是同源策略限制脚本读取不同源响应。标准解法优先是服务端正确配置 CORS;工程上还会结合同源反向代理、本地开发代理、JSONP、postMessage、WebSocket 等方案,但它们适用场景和安全边界不同。
先把跨域分成两个层面:浏览器的同源策略负责限制脚本访问不同源资源,同源由协议、域名、端口三者共同决定;CORS 是浏览器和服务器共同遵守的一套跨域访问协议,真正决定能否被前端读取的是服务端响应头。对于普通接口,最推荐的是服务端按白名单返回 Access-Control-Allow-Origin,并按需配置 Allow-Methods、Allow-Headers、Allow-Credentials、Expose-Headers、Max-Age。简单请求会直接发出,浏览器根据响应头决定是否暴露结果;非简单请求会先发 OPTIONS 预检,确认方法、请求头、凭证策略被允许后才发送真实请求。带 cookie 或 Authorization 等凭证时,前端要设置 credentials,服务端要返回 Access-Control-Allow-Credentials: true,并且 Access-Control-Allow-Origin 不能是星号,必须是明确 origin。无法改后端时可以用同源代理或网关转发;JSONP 只适合 GET 且有脚本执行风险;postMessage 适合窗口、iframe 间通信;WebSocket 不走普通 CORS 预检,但仍应校验 Origin。
同源策略要求协议、域名、端口完全一致,脚本才能自由读取响应、操作 DOM 或访问部分存储。它的目标是隔离不同站点的权限边界,避免一个页面读取另一个站点的敏感数据。跨域时,请求本身很多情况下仍然会被发出,例如表单提交、图片加载、脚本加载、部分接口请求,但浏览器会限制 JavaScript 读取响应内容,因此常见报错其实是浏览器拒绝把响应交给前端代码。
CORS 的核心是由浏览器自动带上 Origin,请求到达服务端后,服务端根据 Origin 判断是否允许该来源访问。如果允许,就在响应中返回 Access-Control-Allow-Origin 等头。浏览器收到响应后再次校验这些头,校验通过才把响应暴露给前端。CORS 不是前端单方面能解决的问题,前端只能声明请求模式和是否携带凭证,最终访问权限由服务端响应头决定。
满足条件的简单请求会直接发送真实请求,例如 GET、HEAD,或者 POST 搭配 application/x-www-form-urlencoded、multipart/form-data、text/plain 这类 Content-Type,并且没有自定义请求头。服务端仍然必须返回正确的 Access-Control-Allow-Origin,否则浏览器会拦截响应。简单请求看起来少了一次 OPTIONS,但并不代表没有安全校验,只是校验发生在真实响应返回之后。
当请求方法是 PUT、DELETE、PATCH,或者带有自定义头、application/json 等不属于简单请求范围的 Content-Type 时,浏览器会先发起 OPTIONS 预检。预检请求中会带 Access-Control-Request-Method 和 Access-Control-Request-Headers,服务端需要返回允许的方法和请求头。只有预检通过,浏览器才发送真实请求;预检失败时,业务接口甚至不会被真正调用。
Access-Control-Allow-Origin 决定允许哪个来源读取响应;Access-Control-Allow-Methods 声明允许的方法;Access-Control-Allow-Headers 声明允许的自定义请求头;Access-Control-Expose-Headers 决定哪些非简单响应头能被前端读取;Access-Control-Max-Age 可以缓存预检结果,降低 OPTIONS 频率。很多问题不是缺一个头,而是请求方法、请求头、凭证、缓存策略之间没有统一配置。
如果请求需要携带 cookie、HTTP 认证信息或依赖浏览器凭证,前端通常要设置 credentials,服务端要返回 Access-Control-Allow-Credentials: true。此时 Access-Control-Allow-Origin 不能使用星号,必须返回具体来源,并且最好基于白名单动态匹配。还要注意 SameSite、Secure、域名和 HTTPS 配置,否则 CORS 头正确,cookie 也可能仍然无法发送或无法写入。
开发环境代理、Nginx 反向代理、Node 中间层或 API 网关,本质是让浏览器只访问同源地址,再由服务端去请求真实后端。服务器到服务器之间没有浏览器同源策略,因此能绕开前端跨域限制。代理适合本地开发、统一网关、隐藏内部服务地址、聚合接口等场景,但它不是权限控制的替代品,仍然要处理鉴权、转发头、缓存、限流和错误透传。
JSONP 利用 script 标签不受同源读取限制的特点,只能发 GET,不能设置普通自定义头,也无法像标准接口那样处理状态码和复杂错误,并且返回内容会作为脚本执行,安全风险较高。postMessage 适合跨窗口、iframe、标签页之间通信,需要严格校验 origin 和消息结构。WebSocket 建连流程不同于普通 fetch,不走标准 CORS 预检,但服务端仍应检查 Origin。
跨域配置过宽会带来数据泄露和权限滥用风险,尤其是把 Access-Control-Allow-Origin 设为任意来源、同时错误处理凭证,或者简单反射请求中的 Origin 而不做白名单校验。CORS 不是防 CSRF 的完整方案,带 cookie 的跨站请求仍要结合 SameSite、CSRF token、鉴权校验和敏感操作二次确认。解决跨域时不能只追求浏览器不报错,还要保证来源、身份和接口能力都受控。
当请求不是简单请求时会预检,例如使用 PUT、DELETE、PATCH,携带自定义请求头,或 Content-Type 是 application/json。浏览器会先确认服务端允许该方法和请求头,再发送真实请求。
因为带 cookie 或认证信息的跨域访问风险更高,浏览器要求服务端明确允许具体来源。服务端应基于白名单返回对应 Origin,并返回 Access-Control-Allow-Credentials: true。
不能完整防止。CORS 控制的是浏览器是否把响应暴露给脚本,但跨站请求仍可能携带 cookie 发出。敏感操作仍需 SameSite、CSRF token、鉴权校验和二次确认等措施。
因为跨域校验发生在浏览器收到响应之后。如果服务端没有返回允许当前 Origin 的响应头,浏览器会拒绝把响应交给前端代码,即使服务端已经执行了业务逻辑。
默认情况下前端只能读取少数简单响应头。如果业务需要读取分页、限流、下载文件名或追踪 ID 等自定义响应头,就需要服务端通过 Expose-Headers 显式暴露。