如今的 Web 项目大多采用前后端分离架构,这使得跨域问题变得常见。

CORS (Cross-Origin Resource Sharing) 跨域资源共享,是浏览器的安全机制。当前端(如 http://localhost:3000)请求后端(如 http://localhost:5000)时,因为域名/端口不同,浏览器会阻止请求,除非后端明确允许。

跨域问题有哪些现象

如果出现跨域问题:

  • 前端:浏览器正在访问一个页面 https://www.helloworld.net/special , 此页面中发送了一个后端的一个 http 接口
  • 后端:访问的后端接口为: https://tiger-api.helloworld.net/v1/special/getSpecialList

提示:has been blocked by CORS policy: Response to preflight request does not pass access control check

翻译过来就是:已被 CORS 策略阻止:对请求的响应未通过访问控制检查

如果没有配置相关的跨域参数,是不能访问这个接口。

什么是跨域问题

如果浏览器访问: https://www.helloworld.net/special

在此页面中,可以请求接口:https://www.helloworld.net/getSpecialList

它们的协议、主机和端口都是相同的,是可以请求成功的。否则,不可以访问。

注意:是在浏览器中

同源策略保证,协议主机端口完全相同,才可以互相访问。否则只要有一个不同,是不能访问的。

img

如何解决跨域问题

通过上面的图,浏览器拦截了响应,导致整个 http 请求没有走通。

如果在响应头中添加一些特殊的字段,浏览器看到这些字段,就不拦截了,跨域问题就解决了。

解决跨域问题,就是在后端加一些配置,在响应头中添加了一些特殊的响应头。

img

只要后端在响应的时候,在响应头添加以下字段,就可以解决跨域问题:

  • access-control-allow-orign:该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
  • access-control-allow-credentials:该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可
  • Access-Control-Allow-Methods:该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次”预检”请求。

其实最重要的就是 access-control-allow-origin 字段,添加一个 * ,允许所有的域都能访问。

实践案例

环境变量配置(.env)

1
2
3
4
5
# 开发环境
ALLOWED_ORIGINS=http://localhost:3000,http://127.0.0.1:3000

# 生产环境
ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com

后端 CORS 中间件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const cors = require('cors');

// 获取允许的来源列表
function getAllowedOrigins() {
const origins = process.env.ALLOWED_ORIGINS || 'http://localhost:3000';
return origins.split(',').map(origin => origin.trim());
}

// CORS 配置
app.use(cors({
// 动态验证请求来源
origin: (origin, callback) => {
const allowedOrigins = getAllowedOrigins();

// 允许无 origin 的请求(Postman、curl、服务端请求)
if (!origin) {
return callback(null, true);
}

// 检查来源是否在白名单中
if (allowedOrigins.includes(origin)) {
callback(null, true);
} else {
console.warn('⚠️ CORS 拒绝访问');
console.warn(' 请求来源:', origin);
console.warn(' 允许来源:', allowedOrigins.join(', '));
callback(new Error('不允许的 CORS 来源'));
}
},

// 允许携带认证信息(cookies、authorization headers)
credentials: true,

// 允许的 HTTP 方法
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'],

// 允许的请求头
allowedHeaders: [
'Content-Type',
'Authorization',
'X-Requested-With',
'Accept'
],

// 暴露的响应头(前端可访问)
exposedHeaders: ['Content-Range', 'X-Content-Range'],

// 预检请求缓存时间(秒)
maxAge: 86400 // 24小时
}));

配置说明

配置项 说明 常用值
origin 允许的来源 函数动态判断或字符串
credentials 是否允许携带凭证 true/false
methods 允许的 HTTP 方法 ['GET', 'POST', ...]
allowedHeaders 允许的请求头 ['Content-Type', ...]
exposedHeaders 前端可访问的响应头 ['X-Total-Count']
maxAge 预检缓存时间(秒) 86400

前端配置

1
2
3
4
5
6
7
8
fetch('http://localhost:5000/api/data', {
method: 'POST',
credentials: 'include', // 携带 cookies
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ data: 'value' })
});

常见问题

Q:localhost 和 127.0.0.1 需要分别配置吗?

A: 是的,浏览器认为它们是不同的源,都需要添加到白名单。

Q: 为什么 Postman 能访问但浏览器不行?

A: Postman 不受同源策略限制,浏览器会强制执行 CORS 检查。

Q: 配置了 CORS 但还是报错?

A: 检查:

  1. 前端请求是否包含 credentials: 'include'withCredentials: true
  2. 后端是否设置 credentials: true
  3. Origin 是否完全匹配(包括协议、端口)
  4. 是否正确处理 OPTIONS 预检请求

总结

  • 同源策略:协议,主机,端口 三者都相同,就是同一个源,只要有一个不同,就是不同的源,只有同一个源的资源才能互相访问
  • 跨域问题就是浏览器的同源策略造成的
  • 跨域问题的本质 ,就是浏览器拦截了响应,所以后端只需要在响应头中添加相应的字段,就可以解决跨域问题