如何让CORS请求返回HTTP 304而非HTTP 200响应?
跨域请求无法触发304缓存响应问题排查与解决
问题现象
跨域RESTful请求中,尽管请求、响应头(包含If-None-Match和ETag)无变化,请求始终返回200 OK,从未触发304 Not Modified缓存响应。预检请求已通过Access-Control-Max-Age缓存,但所有实际XHR请求均返回200,且此问题仅在跨域场景下出现。
响应头信息
Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: http://www.acme.com Access-Control-Max-Age: 86400 Cache-Control: no-cache Pragma: no-cache Vary: Cookie, Origin
服务端代码片段
import express from 'express'; import cors from 'cors'; const server = express(); server .set( 'etag', 'strong' ) .use( cors({ credentials: true, maxAge: 86400, origin: 'http://www.acme.com' }) ) .use(( req, res, next ) => { res.setHeader( 'Cache-Control', 'no-cache' ); res.setHeader( 'Pragma', 'no-cache' ); res.setHeader( 'Vary', `Cookie, ${ res.get( 'Vary' ) }` ); next(); }) ...
问题根源
核心问题在于设置的Cache-Control: no-cache和Pragma: no-cache头。虽然no-cache理论上允许缓存验证,但在跨域带凭证(credentials: true)的场景下,浏览器与服务器的缓存交互逻辑会被这些头干扰,导致服务器无法正确触发304响应逻辑。同时,这类头会强制浏览器发起完整请求,弱化了ETag的缓存验证作用。
解决方案
1. 调整缓存控制头配置
移除强制不缓存的设置,替换为允许私有缓存且必须验证的配置,适配跨域带凭证的场景:
.use(( req, res, next ) => { // 允许私有缓存(仅当前用户可访问),且每次使用前必须向服务器验证 res.setHeader( 'Cache-Control', 'must-revalidate, private' ); // 移除Pragma头(仅兼容旧浏览器,现代浏览器无需) res.setHeader( 'Vary', `Cookie, ${ res.get( 'Vary' ) }` ); next(); })
2. 确认ETag处理逻辑生效
你已通过server.set('etag', 'strong')开启强ETag验证,Express默认会自动处理If-None-Match请求头:当请求携带的If-None-Match与响应ETag一致时,自动返回304 Not Modified。需确保后续路由处理中未覆盖ETag或修改响应体内容(响应体细微变化会导致ETag改变,无法匹配)。
3. 确保跨域缓存兼容性
跨域带凭证的请求需满足:
- 响应头包含
Vary: Origin, Cookie(你的代码已配置,避免后续逻辑覆盖) - 缓存控制头不能包含
no-store(禁止缓存资源的指令)
验证步骤
修改配置后重新测试:
- 首次请求:服务器返回
200 OK,携带ETag和调整后的Cache-Control头 - 二次请求:浏览器自动携带
If-None-Match头(值为首次请求的ETag) - 服务器对比ETag一致后返回
304 Not Modified,浏览器使用本地缓存
内容的提问来源于stack exchange,提问作者Stephen Isienyi
相关产品推荐
相关产品推荐

