深入理解 CORS 预检请求的 TTL 缓存机制

300 阅读4分钟

跨源资源共享(CORS)的预检请求(Preflight Request)机制中,TTL 缓存机制的核心作用是减少不必要的 OPTIONS 请求,从而提高性能、降低服务器压力。

一、CORS 和预检请求回顾

在现代 Web 应用中,出于安全原因,浏览器会对**跨源请求(Cross-Origin Requests)**实施限制。为解决合法跨域通信需求,W3C 提出了 CORS(Cross-Origin Resource Sharing) 规范。

什么是预检请求?

某些跨源请求会被认为是“有风险的请求”,例如:

  • 使用了 PUT, DELETE, PATCH 等非常规方法
  • 使用了自定义头(如 X-Custom-Header
  • Content-Type 不属于 application/x-www-form-urlencoded, multipart/form-data, text/plain 三种之一

这类请求在正式发送之前,浏览器会自动先发送一个 OPTIONS 请求,即“预检请求(Preflight Request)”,以确认服务器是否允许这个跨域行为。


二、预检请求的性能瓶颈

虽然预检是为了安全,但频繁的预检请求带来了显著的性能问题:

  • 多了一次 RTT(Round-Trip Time),延迟提升
  • OPTIONS 请求本身消耗服务器资源
  • 用户体验受影响,尤其在频繁调用 API 的 SPA 应用中更明显

举例说明:

客户端请求:OPTIONS /api/user  (预检)
服务端响应:HTTP 200 + Access-Control-Allow-* 头
客户端才正式发:GET /api/user

如果每次请求都进行一次预检,效率极低。


三、Access-Control-Max-Age 与 TTL 缓存机制

为缓解上述问题,CORS 引入了 Access-Control-Max-Age 响应头,允许浏览器在一段时间内缓存预检请求的结果,从而跳过后续的 OPTIONS 请求。

响应头定义:

Access-Control-Max-Age: 86400

表示该响应的预检结果可以被缓存 86400 秒(即 24 小时)


四、TTL 缓存机制的工作原理

下面我们从 浏览器实现角度,详细讲解 TTL 缓存如何影响预检逻辑:

1. 缓存命中规则

浏览器会以以下维度缓存预检请求的结果:

  • 请求的 Origin
  • 请求的 Method
  • 请求的 自定义 Headers
  • 请求的 URL

缓存记录会绑定在这些组合上。例如: 如果你请求 https://api.example.com/data,用 PUT 方法加了一个 X-Token 头,预检结果会单独为此组合缓存。

2. 缓存位置:浏览器内部内存或磁盘缓存

缓存一般存储于浏览器的内存中,或者在某些情况下写入磁盘(开发者模式下通常禁用磁盘缓存)。当 TTL 时间到达后,缓存会被销毁。

3. 浏览器行为逻辑示意:

第一次跨域请求 → 浏览器发送 OPTIONS → 服务器返回 200 + Access-Control-Max-Age → 浏览器缓存响应

第二次相同请求 → 浏览器检查缓存 → 若 TTL 未过,直接跳过 OPTIONS,发送正式请求

五、示例:如何设置 TTL 缓存

后端示例(Node.js + Express):

app.options('/api/data', (req, res) => {
  res.set({
    'Access-Control-Allow-Origin': 'https://example.com',
    'Access-Control-Allow-Methods': 'GET, POST, PUT',
    'Access-Control-Allow-Headers': 'Content-Type, X-Token',
    'Access-Control-Max-Age': '86400' // 缓存一天
  })
  res.sendStatus(200)
})

后续 24 小时内,只要是相同条件的请求,不再触发 OPTIONS 请求。


六、注意事项与误区

1. Access-Control-Max-Age 是服务器端的承诺,不是浏览器的强制缓存周期

部分浏览器会设定最大上限,即使你写了几天的时间,也可能被强制缩短。例如:

  • Safari:最长 600 秒
  • Chrome/Firefox:一般允许更长(最大 24 小时)

2. 请求变化会导致缓存失效

哪怕是 header 多了一个字段,method 变了,URL 路径变了,都会重新触发预检。

3. 开发环境中的干扰

Chrome 的 “Disable cache” 开启时,预检缓存会被禁用。很多人误以为设置失败,其实是开发者工具影响了行为。


七、性能提升效果

对高频接口(如每次点击都会调用的搜索接口),加上合理的 TTL,性能提升显著:

  • 减少预检次数 → 降低网络开销和延迟
  • 降低服务器压力 → 特别是预检接口不用走业务逻辑,但仍要处理请求

八、最佳实践总结

项目建议
设置缓存建议 Access-Control-Max-Age: 600 或更长
控制 header尽量精简自定义 header,减少触发预检概率
method 选择使用 GETPOSTContent-Typeapplication/x-www-form-urlencoded
缓存命中优化保持接口请求一致性(路径、方法、headers)
环境检测测试时注意关闭“Disable cache”选项

九、结语

Access-Control-Max-Age 并不是什么神秘参数,而是浏览器为优化跨域请求性能所设计的 TTL 缓存机制。通过合理利用该机制,可以大幅减少不必要的 OPTIONS 请求,从而提升前端性能,优化用户体验。