关键是要用稳定设备标识(mobile/desktop)替代原始ua构建缓存键,并显式加入$device、设置vary: x-device-type响应头,避免缓存分裂;同时刷新需匹配完整key,如web_v1:https://example.com/list:mobile。

要让缓存键真正区分手机端和电脑端,关键不是“识别UA”,而是提取稳定、语义明确的设备类型标识,并确保该标识实际影响后端响应内容。否则缓存被拆开反而降低命中率。
用 map 指令归一化 UA 为 device 类型
直接拼接 `$http_user_agent` 做缓存键极不可靠——它长度不定、含大量无意义字段(如浏览器版本、微信/钉钉内置标识),极易导致缓存分裂。应在 `http{}` 块中统一映射:map $http_user_agent $device {
~*"(iPhone|iPad|iPod|Android|Mobile)" "mobile";
~*"(Windows NT|Mac OS X|Linux|Win64)" "desktop";
default "desktop";
}
这样所有主流移动设备归为 mobile,桌面系统归为 desktop,屏蔽 UA 微小差异干扰。
在 proxy_cache_key 中显式加入 $device
仅靠 UA 判断不够,必须把 `$device` 显式写进缓存键,且位置固定、不含动态变量:proxy_cache_key "$scheme$request_method$host$request_uri$is_args$args:$device";
若还需隔离登录态,可追加 ::$cookie_user_id(未登录时设为 "guest");
若页面按语言差异化,可加 ::$http_accept_language,但建议只取主语言(如 zh 或 en),避免因 zh-CN,zh;q=0.9 等细微差异造成分裂。
⚠️切勿包含 $request_time、 $msec、$date_gmt 等实时变量,否则缓存永不命中。
同步设置 Vary 和自定义响应头
光改 key 不够,CDN 和浏览器必须知道“这个响应依赖设备类型”。需两步配合:- 添加响应头告知缓存代理:
add_header Vary "X-Device-Type"; - 同时输出设备标识便于验证和调试:
add_header X-Device-Type $device;
这样 CDN 才会将 /home:mobile 和 /home:desktop 视为两个独立缓存分支,而不是仅靠 URL 区分。
刷新与验证必须匹配完整 key
一旦 `$device` 进入缓存键,传统“按 URL 清缓存”就失效了。刷新操作必须基于完整 key,例如:-
web_v1:https://example.com/list:mobile web_v1:https://example.com/list:desktop
建议在 key 开头加业务前缀(如 web_v1:),便于批量 purge 和日志追踪。
上线前务必用 curl -I 验证:
- 响应头是否含
Vary: X-Device-Type - 不同 UA 请求是否返回不同
X-Device-Type值 -
X-Cache头是否在切换 UA 后出现HIT/MISS符合预期
不复杂但容易忽略











