nginx缓存要真正区分不同参数页面,必须组合使用$uri与清洗后的$args,剔除utm_*/ts等干扰参数,避免仅用$uri(忽略参数)或直接拼接原始$args(导致碎片化),推荐proxy_cache_key "$scheme$request_method$host$uri$is_args$clean_args"。

要让 Nginx 缓存真正区分不同参数的页面,不能只靠 $uri 或只靠 $args,关键在于把两者**有意识地组合使用,并控制哪些参数参与区分、哪些必须剔除**。因为 $uri 本身不含查询参数,而原始 $args 又容易引入干扰,直接拼接会出错或碎片化。
为什么不能只用 $uri
$uri 是解码后的路径部分(如 /search),不包含任何查询参数。这意味着:
-
/search?s=nginx和/search?s=cache的$uri都是/search,缓存键完全一样 - 所有搜索词、分页号(
?page=2)、筛选条件(?category=books)都会被忽略 - 结果就是:用户看到的永远是第一个请求缓存下来的页面,严重错乱
为什么不能直接拼 $uri + $args
看似合理,但实际风险高:
-
$args原始值保留参数顺序和编码格式,a=1&b=2和b=2&a=1会被当成两个 key,即使后端逻辑等价 - 含
utm_source=google、ts=1727729400这类不改变内容的参数,会让同一页面生成几十个无效缓存项 - 空参数(如
?v=)或缺失参数(?v)可能造成 key 不稳定,影响命中率
推荐做法:用 $uri 拼接清洗后的 $args
核心思路是——保留业务关键参数,剔除跟踪/防缓存类参数,再与 $uri 安全组合:
- 在
http{}块中用map预处理$args,生成干净的$clean_args - 示例(剔除 utm_*、ts、random、ref 等):
map $args $clean_args {
default "";
"~^(.*&)?(utm_[^&=]*|ts|random|ref)=[^&]*(?:&.*)?$" "$1";
"^$" "";
} - 缓存键写成:
proxy_cache_key "$scheme$request_method$host$uri$is_args$clean_args"; - 这样既利用
$uri精确标识路径,又只让真正影响内容的参数参与缓存区分
对特定参数做语义归一化(进阶)
有些参数需标准化后再加入 key,避免因格式差异导致重复缓存:
- 版本号参数
v:空值统一为v0,避免?v=和无v参数被视为不同 - 设备标识:用
map $http_user_agent $device提取mobile/desktop,而非直接塞完整 UA 字符串 - 登录态:用
md5($cookie_user_id)替代明文 cookie,兼顾隔离性与安全性











