nginx跨域缓存需同时满足cors与http缓存规范:必须设vary: origin以隔离不同源缓存;credentials为true时禁用通配符,改用map白名单动态设置access-control-allow-origin;options预检响应应缓存30天并返回204;自定义响应头需通过access-control-expose-headers显式声明方可被js读取。

跨域资源在 Nginx 中缓存时,必须同时满足 CORS 规范和 HTTP 缓存机制的要求,否则容易出现缓存污染、凭证失效或浏览器拒绝读取响应等问题。核心在于:CORS 响应头(尤其是 Access-Control-Allow-Origin)必须参与缓存键计算,且不能被 CDN 或代理无差别缓存。
必须设置 Vary: Origin 头部
当 Access-Control-Allow-Origin 的值是动态的(如基于 $http_origin 设置),Nginx 必须显式声明:
add_header Vary 'Origin' always;- 该头部告诉浏览器和中间缓存(包括 CDN):“这个响应是否可缓存,取决于请求中的 Origin 字段”
- 缺少
Vary: Origin会导致不同源的请求共用同一份缓存,例如用户 A(origin: https://a.com)的响应被缓存后,用户 B(origin: https://b.com)可能收到本该拒绝其访问的响应头,造成安全泄露
避免通配符 * 与 Credentials 共存时的缓存陷阱
若配置了 Access-Control-Allow-Credentials true,则 Access-Control-Allow-Origin 不能为 * —— 这是浏览器强制要求。此时缓存需特别注意:
- 使用
map指令白名单匹配合法 origin,并动态赋值:map $http_origin $cors_origin {<br> default "";<br> ~^https?://(www\.)?myapp\.com(:[0-9]+)?$ $http_origin;<br>} - 配合
add_header Access-Control-Allow-Origin $cors_origin always;和Vary Origin,确保每个合法源获得专属缓存条目 - 若 origin 不在白名单中,$cors_origin 为空,Nginx 不发送 CORS 头,该响应默认不参与跨域流程,可按普通资源缓存(但通常应返回 403)
预检请求(OPTIONS)建议单独缓存
浏览器对非简单请求会先发 OPTIONS 预检,这类响应不含业务数据,适合长期缓存以减少服务端压力:
- 在 location 块中捕获 OPTIONS 请求:
if ($request_method = 'OPTIONS') {<br> add_header Access-Control-Allow-Origin $cors_origin;<br> add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE';<br> add_header Access-Control-Allow-Headers 'Content-Type, Authorization';<br> add_header Access-Control-Max-Age 1728000;<br> add_header Vary Origin;<br> return 204;<br>} - 搭配
expires 30d;或add_header Cache-Control 'public, max-age=2592000';,让预检结果缓存 30 天 - 注意:204 响应体为空,必须用
always参数确保头部在错误响应中也生效(如 Nginx 内部重定向失败时)
暴露响应头需显式声明 Expose-Headers
前端 JavaScript 只能读取响应头中被明确列出的字段(如 X-Total-Count、ETag)。若需缓存并复用这些字段:
- 添加:
add_header Access-Control-Expose-Headers 'X-Total-Count, ETag, X-RateLimit-Remaining'; - 这些字段将随缓存一同保存;浏览器从缓存读取响应时,也能通过
response.headers.get('X-Total-Count')正常获取 - 未列入
Expose-Headers的自定义头,即使存在于原始响应中,缓存后也无法被 JS 访问











