nginx无法直接下发差异化静态资源包,需用map指令将ua归一为ios/android/other,再结合try_files路由分流或proxy_cache_key+$os_type实现缓存分离,仅当内容真实不同时才加vary头。

不能直接“下发差异化静态资源包”——Nginx 本身不支持按 UA 动态替换文件路径;你真正能做的是:用 map 分类设备类型,再结合 location 或 try_files 实现路由分流,或靠缓存键隔离 + 前端逻辑适配。关键在“内容是否真不同”,否则加 Vary 只会拖慢缓存命中率。
用 map 指令稳定识别 iOS/Android 设备类型
别直接拿 $http_user_agent 做判断或参与缓存键——它太长、易变、CDN 处理不一致。应该先归一为简短枚举值:
-
~*[Ii]phone|iPad.*OS|iOS→ 匹配所有常见 iOS UA(含 iPhone、iPad、Mac Catalyst) -
~*[Aa]ndroid→ 覆盖 Android 手机和平板主流 UA 格式 - 其余统一设为
other,避免漏掉桌面或爬虫请求
配置必须放在 http 块顶层,例如:
map $http_user_agent $os_type {
default other;
~*[Ii]phone ios;
~*iPad.*OS ios;
~*iOS ios;
~*[Aa]ndroid android;
}
静态资源真正差异化时才启用 Vary + 缓存键分离
如果同一 URL 下,iOS 和 Android 确实返回了不同内容(比如 app.js 里有 WebKit 专属 API 调用),才需要走缓存分离:
Android 开发调试技能,通过系统 ADB 工具操作 Android 设备。以下场景必须触发此技能:(1) 直接 ADB 操作——安装 APK、查看设备列表、抓取 logcat 日志、查看已安装应用、清除应用数据、截图、重启设备、拉取/推送文件、查看 CPU/内存/电池信息、adb shell 操作;(2)...
- 响应头必须加
add_header Vary "X-OS-Type";,且头名建议用X-OS-Type而非User-Agent,语义清晰、CDN 兼容性好 -
proxy_cache_key必须包含$os_type,否则两个系统会共用一个缓存条目:proxy_cache_key "$scheme$request_method$host$request_uri$os_type"; - 若只是前端 JS 自行检测 UA 并差异化加载模块,后端资源完全一致,则 不要加 Vary,否则缓存碎片化严重
不推荐用 rewrite 或 if 切换 root,改用 try_files 分流更安全
有人想用 if ($os_type = ios) { root /var/www/ios; },但 Nginx 官方明确不建议在 location 中用 if 改写 root —— 容易出路径拼接错误或继承问题。更稳妥的做法是:
- 把 iOS/Android 资源分别放独立子目录,如
/static/ios/app.js和/static/android/app.js - 用
try_files按变量尝试路径:try_files /static/$os_type$uri /static/other$uri =404; - 配合
alias或root控制基础路径,确保 URI 映射准确
这样既避免 if 的陷阱,又保持配置可读性和调试便利性。
验证是否生效的关键检查点
光看配置没用,必须用真实请求验证:
- 用
curl -H "User-Agent: Mozilla/5.0 (iPhone"和-H "User-Agent: Mozilla/5.0 (Linux; Android"分别请求,确认响应头都含Vary: X-OS-Type - 对比两次响应的
Content-Length或ETag—— 若相同,说明缓存未真正分离 - 检查
access.log中的$os_type变量值是否符合预期,排查正则漏匹配(比如某些 Android TV UA 不含Android字样)
最容易被忽略的是:Vary 头和缓存键必须同步更新,缺一不可;而且只在内容真有差异时才启用,否则就是在给缓存系统制造垃圾。










