设置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
相关产品推荐
相关产品推荐

