You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

设置Vary: Origin后带ETag的响应仍缓存Access-Control-Allow-Origin引发CORS错误

问题根因

这是Mac端Chrome浏览器特有的CORS缓存冲突问题,触发原因有两个:

  • 你配置了Access-Control-Max-Age: 7200,Chrome会将预检请求(OPTIONS)的结果缓存2小时,缓存key仅关联请求URL和请求方法,不同Origin的相同请求会直接命中缓存,复用之前返回的Access-Control-Allow-Origin值。
  • 两个站点的相同接口返回的ETag完全一致,Chrome收到304响应时会直接复用之前缓存的全部响应头,无视服务端返回的新CORS头,即使你已经配置了Vary: Origin。
  • 你提供的两个响应头X-Request-Id完全一致,也验证了第二个请求没有触发Rails的完整处理流程,是缓存返回的旧结果。

解决方案

1. 调整预检请求缓存时长

修改rack-cors配置,降低max_age参数值,生产环境建议设置为60秒以内,开发环境可直接设置为0禁用预检缓存:

# config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'https://mywebsite.com', 'https://app.mywebsite.com'
    resource '*', 
      headers: :any, 
      methods: [:get, :post, :patch, :put, :delete], 
      credentials: true, 
      max_age: 60 # 将原有的7200修改为60或更小值
  end
end

2. 补充Vary响应头字段

除Origin外,将Access-Control-Request-Method和Access-Control-Request-Headers加入Vary头,确保不同源、不同请求方法的CORS请求不会共用缓存:
可以在应用控制器统一添加:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  after_action do
    response.set_header('Vary', 'Origin, Access-Control-Request-Method, Access-Control-Request-Headers')
  end
end

也可以直接在Apache配置中修改响应头规则。

3. 区分不同Origin的ETag(可选)

如果上述步骤仍未解决,给不同Origin的响应生成不同ETag,避免Chrome因ETag相同复用旧响应头:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action do
    request.etag = "#{request.etag}-#{request.origin}" if request.origin.present?
  end
end

4. 检查Apache缓存规则

确认前置的Apache服务没有开启API接口的缓存规则,不要缓存任何CORS相关响应头,确保所有API请求的响应头都是Rails服务实时返回的。

验证方法

打开Chrome开发者工具,在Network面板勾选「Disable cache」,清除现有浏览器缓存后,分别测试两个站点的接口请求,确认每个请求返回的Access-Control-Allow-Origin值与请求头的Origin值一致即可。

内容的提问来源于stack exchange,提问作者RobEW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 09:18:00