浏览器缓存不主动根据user-agent或accept-encoding返回不同产物,而是由服务器通过vary响应头声明缓存键维度,配合真实差异化响应实现缓存区分;vary: accept-encoding使gzip与br版本独立缓存,vary: user-agent易致缓存碎片化,需后端真实适配并标准化ua以避免无效分裂。

浏览器缓存本身不主动“根据 User-Agent 或 Accept-Encoding 返回不同产物”,它只负责存储和复用响应。真正实现差异化返回与缓存区分的,是服务器(如 Nginx、CDN 或应用后端)配合 Vary 响应头协同完成的——Vary 是告诉缓存系统:“这个响应是否可用,取决于请求里这些头字段的值是否匹配”。
理解 Vary 的作用机制
Vary 不是让浏览器“动态生成内容”,而是声明缓存键(cache key)的构成维度:
- 当响应头包含 Vary: Accept-Encoding,缓存会把
Accept-Encoding的实际值(如gzip、br、空)纳入缓存标识。同一 URL,gzip版本和br版本会被存为两个独立缓存项。 - 当响应头包含 Vary: User-Agent,缓存会按 UA 字符串做区分。但 UA 取值高度离散(如含版本号、设备标识),极易导致缓存碎片化,命中率骤降。
- 缓存(包括浏览器、CDN、Nginx proxy_cache)在收到新请求时,先提取请求中的对应头字段值,再查找“完全匹配该值”的缓存项;不匹配则回源。
服务端如何配合生成差异产物
仅设 Vary 不够,后端必须真实依据请求头返回不同内容:
-
Accept-Encoding:Web 服务器(如 Nginx)需启用压缩模块,并根据客户端请求的
Accept-Encoding自动选择 gzip / brotli / 不压缩;后端应用若自行压缩,也需读取该头并输出对应Content-Encoding和正文。 -
User-Agent:需业务逻辑识别 UA(如用正则或库判断是否为移动端/微信内置浏览器),然后返回适配的 HTML、JS 或 CSS。例如:
– 移动端 UA → 返回精简版 JS + viewport 优化 HTML
– 桌面 Chrome → 返回完整功能 JS + WebAssembly 模块 - 注意:若后端对所有 UA 都返回完全相同的资源,却设置
Vary: User-Agent,只会徒增缓存分裂,毫无意义。
Nginx 中正确配置 Vary 与缓存协同
要确保 Vary 生效,必须让缓存键(proxy_cache_key)与 Vary 声明的字段严格一致:
- 若响应头设
Vary: Accept-Encoding,缓存键中必须包含$http_accept_encoding:
- 若响应头设
Vary: User-Agent,且确有必要(如 SSR 渲染不同布局),建议先标准化 UA(避免版本号波动),再写入缓存键:
~*mobile|android|iphone|ipad "mobile";
default "desktop";
}
proxy_cache_key "$scheme$request_method$host$request_uri$ua_type";
add_header Vary "User-Agent";
- 禁止仅用
proxy_hide_header Vary来“绕过问题”——这会让下游缓存误判为可通用缓存,引发内容错乱(如手机用户看到桌面版 JS)。
静态资源建议最小化 Vary
对于 JS/CSS/图片等静态资源,通常无需 Vary User-Agent:
- 现代前端构建已通过响应式设计、特性检测替代 UA 判断;
- 压缩格式由 CDN 或 Nginx 统一处理,只需
Vary: Accept-Encoding即可; - 若资源确实无差别(如一个 PNG 图片),连
Vary: Accept-Encoding也可省略——但需确认服务器未对同一路径返回不同编码版本。











