要实现针对特定User-Agent的缓存分离,关键是先用map将UA归一化为有限稳定类别(如ios、android、desktop),再以此变量构建cache_key和Vary头;必须同步配置add_header Vary "Device-Type" always与proxy_cache_key包含$device_type,避免默认key导致碎片化,并通过curl和日志验证分类与命中效果。

要实现针对特定 User-Agent 的缓存分离,关键不是直接把 $http_user_agent 塞进缓存键,而是先做归一化——用 map 把千变万化的 UA 字符串映射为有限、稳定、语义明确的类别(比如 ios、android、desktop),再让这个类别变量参与缓存键和 Vary 头构建。否则缓存会爆炸,命中率趋近于零。
用 map 归类 UA,生成稳定设备类型变量
在 http 块顶层定义映射,避免重复编译正则,也便于统一维护:
- 优先匹配高区分度特征:iOS 设备含
iPhone、iPad;Android 含Android;Windows Phone、Firefox Mobile 等单独列出 - 兜底规则必须明确,例如
default "desktop",不能留空或写模糊值 - 变量名要合法(无空格、不以数字开头),且后续所有地方保持一致,比如定义为
$device_type
同步配置 Vary 和 proxy_cache_key
Vary 头和缓存键必须严格对齐,缺一不可:
- 加
add_header Vary "Device-Type" always;——always很重要,确保 304、404 等响应也携带,避免边缘节点缓存行为不一致 -
proxy_cache_key中必须包含$device_type,例如:"$scheme$request_method$host$request_uri$http_origin$device_type" - 如果还依赖其他头(如
Origin),Vary和proxy_cache_key都得同步体现,否则缓存隔离失效
避免默认 cache_key 引发碎片化
Nginx 默认 proxy_cache_key 包含完整 $request_uri(含 query 参数)和全部请求头,极易导致同一资源生成大量无效缓存项:
- 静态资源通常无需按参数区分,可显式剔除干扰项,比如不用
$args,也不把$http_cookie或$http_referer加进去 - 若需支持登录态差异化(如会员/非会员),应提取稳定标识(如
map $cookie_sessionid $cache_user_id { ... }),而非直接用原始 Cookie - 缓存键越精简、越稳定,命中率越高;过度泛化(如全量 UA)和过度细化(如带随机 utm 参数)都会损害性能
验证与调试建议
上线前建议用真实 UA 请求并检查响应头与缓存行为:
- 用
curl -H "User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X)" http://example.com/logo.png -I查看是否返回Vary: Device-Type - 查看 Nginx 日志中的
$upstream_http_x_cache(需后端配合)或$sent_http_x_cache(自定义 header)确认是否命中 - 对比不同 UA 的缓存键哈希值(可通过调试日志或第三方模块输出),确认
$device_type确实生效且分类合理


















