跨域问题终极指南:从原理到五种解决方案实战(CORS / JSONP / Nginx / WebSocket / postMessage)

202 阅读5分钟

你是不是也被这些报错折磨过?

Access to XMLHttpRequest has been blocked by CORS policy...

跨域几乎是每一个前端工程师都会遇到的第一道坎。本文将用 面试 + 实战 + 原理 + 场景 的方式,带你彻底吃透跨域,并掌握五种主流解决方案:

JSONP、CORS、Nginx 反向代理、WebSocket、postMessage


一、什么是跨域?(一句话讲清楚)

跨域 = 浏览器的同源策略不允许一个源去读取另一个源的响应结果

同源策略(Same-Origin Policy)

只有当以下三者完全一致时,才是同源:

协议 + 域名 + 端口
URL是否同源原因
http://localhost:3000基准
http://localhost:3000/api路径不同不影响
https://localhost:3000协议不同
http://127.0.0.1:3000域名不同
http://localhost:8080端口不同

本质:请求可以发出去,但浏览器不让你读响应结果。


二、JSONP:最古老的跨域方案(利用 script 标签不受同源限制)

1. 原理

<script> 标签的 src 不受同源策略限制,可以加载任意域名的资源。

浏览器:

<script src="http://api.xxx.com/user?callback=cb"></script>

服务器返回:

cb({ name: 'Tom', age: 18 })

前端定义:

function cb(data) {
  console.log(data)
}

2. 特点

优点缺点
实现简单只支持 GET
兼容性好安全性差
老项目常见无法处理复杂请求

3. 适用场景

  • 老项目兼容方案
  • 第三方接口只支持 JSONP

三、CORS:最主流、最标准的跨域方案 ⭐⭐⭐⭐⭐

跨资源共享(Cross-Origin Resource Sharing)

1. 原理

服务器通过响应头告诉浏览器:

哪些域名可以访问我
哪些请求方式可以用
哪些请求头是被允许的

也就是说:

跨域不是前端解决的,而是后端“放行”的结果

当浏览器发起跨域请求时:

  1. 浏览器先检查响应头
  2. 如果看到这些头:
Access-Control-Allow-Origin: http://localhost:3000

就会理解为:

「服务器允许 http://localhost:3000 这个网站访问它」

于是浏览器才会把响应结果交给前端 JS 使用。


2. 简单请求 vs 预检请求

简单请求

满足以下条件时,浏览器会直接发送请求,不会多做检查:

  • 请求方法是:

    • GET
    • POST
    • HEAD
  • 请求头是“普通的 header”,比如:

    • Content-Type
    • Accept
  • Content-Type 只能是:

    • text/plain
    • application/x-www-form-urlencoded
    • multipart/form-data

满足这些条件:

浏览器认为:这个请求是安全的 → 直接放行

所以你看到的效果是:

一次请求就成功返回数据


预检请求(OPTIONS)

当你做了“更危险”的事情时,比如:

  • 使用 PUT / DELETE
  • 携带 Authorization
  • 自定义请求头

浏览器会变得非常谨慎:

先不发真正请求,而是先问服务器一句话:

OPTIONS /login

意思是:

「我想用 Authorization 头来访问你,你允许吗?」

服务器返回:

Access-Control-Allow-Headers: Authorization

浏览器看到后:

「允许的,那我才真正发送 POST /login」

所以你会看到:

一次接口调用,其实发了两次请求(OPTIONS + 真正请求)


3. Node 实战

app.use((req, res, next) => {
  // 允许哪个前端域名访问我
  res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');

  // 允许哪些请求方式
  res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');

  // 允许前端携带哪些请求头
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');

  // 是否允许携带 cookie
  res.setHeader('Access-Control-Allow-Credentials', 'true');

  next();
});

这一段的意思是:

后端明确告诉浏览器:

  • 哪个前端能访问
  • 用什么方式访问
  • 能带什么信息访问
  • 能不能带登录态访问

浏览器看到后:

「安全,放行!」


四、Nginx 反向代理:开发环境最舒服的方案

1. 原理

浏览器只和 Nginx 通信 → Nginx 再请求后端 → 浏览器以为是同源

浏览器 → Nginx(前端域名) → 后端服务器

2. 配置示例

server {
  listen 80;
  server_name www.front.com;

  location /api/ {
    proxy_pass http://api.back.com/;
    proxy_set_header Host $host;
  }
}

前端请求:

axios.get('/api/user/list')

五、WebSocket:天生跨域的通信协议

1. 原理

WebSocket 不受同源策略限制:

const ws = new WebSocket('ws://api.xxx.com:8080');

建立长连接后即可双向通信。

2. 特点

优点缺点
天生跨域不适合普通接口
实时通信维护成本高

3. 场景

  • 聊天室
  • 实时推送
  • 股票行情

六、postMessage:iframe 跨域通信方案

1. 原理

// 父页面
iframe.contentWindow.postMessage('hello', 'http://child.com');

// 子页面
window.addEventListener('message', e => {
  console.log(e.data);
});

2. 特点

优点缺点
安全可控使用复杂
支持双向通信仅限页面通信

3. 场景

  • 微前端
  • 第三方嵌入页面
  • 登录中台系统

七、五种方案对比总结(面试必背)

方案原理使用场景面试评价
JSONPscript 无跨域限制老项目
CORS响应头授权正式生产⭐⭐⭐⭐⭐
Nginx服务端转发开发环境⭐⭐⭐⭐
WebSocket天生跨域协议实时通信⭐⭐⭐
postMessage页面通信微前端⭐⭐

八、面试标准回答模板

跨域是浏览器同源策略导致的安全限制。实际项目中主要通过 CORS 在服务端设置响应头解决,区分简单请求和预检请求。本地开发阶段使用 Nginx 或 Vite Proxy 代理避免跨域。JSONP 只支持 GET 已逐渐淘汰,WebSocket 天生跨域用于实时通信,postMessage 常用于 iframe 或微前端页面通信。


九、结语

跨域不是 Bug,而是浏览器的安全设计。

真正的高手不是背方案,而是:

知道什么时候用哪种方案,为什么这样用。

如果你觉得这篇文章对你有帮助,欢迎点赞 + 收藏 + 关注,后续我会持续输出:

  • 前端面试高频知识点深度解析
  • React/Vue 项目实战拆解
  • 大厂面试通关路线图