减少 CORS 预请求次数

通过简单请求和缓存策略减少 CORS 预检请求

问题

跨域请求时,浏览器会发送 OPTIONS 预检请求,增加了请求延迟。如何减少这些预请求的次数?

解答

1. 理解预检请求的触发条件

只有”非简单请求”才会触发预检。简单请求需满足:

  • 方法为 GETHEADPOST 之一
  • 仅使用安全请求头:AcceptAccept-LanguageContent-LanguageContent-Type
  • Content-Type 仅限:application/x-www-form-urlencodedmultipart/form-datatext/plain

2. 方法一:使用简单请求

// ❌ 会触发预检:使用了自定义请求头
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',  // 非简单请求头
    'X-Custom-Header': 'value'           // 自定义请求头
  },
  body: JSON.stringify({ name: 'test' })
});

// ✅ 不触发预检:符合简单请求条件
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: 'name=test&age=18'
});

3. 方法二:设置预检缓存

服务端设置 Access-Control-Max-Age,让浏览器缓存预检结果:

// Node.js Express 示例
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'https://example.com');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  
  // 缓存预检结果 86400 秒(24小时)
  // 在此期间,相同请求不再发送 OPTIONS
  res.header('Access-Control-Max-Age', '86400');
  
  if (req.method === 'OPTIONS') {
    return res.sendStatus(204);
  }
  next();
});

4. 方法三:合并请求

// ❌ 多次请求,多次预检
await fetch('/api/user');
await fetch('/api/orders');
await fetch('/api/products');

// ✅ 合并为一次请求
await fetch('/api/batch', {
  method: 'POST',
  body: JSON.stringify({
    requests: ['/user', '/orders', '/products']
  })
});

5. 方法四:同源代理

通过同源服务器代理请求,完全避免跨域:

// 前端请求同源接口
fetch('/api/proxy/external-data');

// 后端代理转发
app.get('/api/proxy/external-data', async (req, res) => {
  const data = await fetch('https://external-api.com/data');
  res.json(await data.json());
});

关键点

  • 简单请求不触发预检:GET/HEAD/POST + 安全请求头 + 限定 Content-Type
  • Access-Control-Max-Age 可缓存预检结果,单位为秒
  • 避免自定义请求头,尽量用标准头
  • 合并多个 API 请求减少总请求数
  • 同源代理可完全规避 CORS 问题