直接用 $request_uri 哈希最简明,但含时间戳等高频变动参数时需先标准化 URI 再哈希,否则缓存节点漂移、命中率下降;推荐用 map 预处理提取路径并配合 consistent hash 实现稳定负载均衡。

直接用 hash $request_uri 是最简明有效的做法,但若 URL 中含高频变动参数(如时间戳、追踪参数),需先标准化再哈希,否则缓存节点会频繁漂移、命中率不升反降。
为什么不能直接用带参 URL 做哈希
像 /img/logo.png?t=1712345678 或 /api/data.json?utm_source=weibo&v=2.1 这类 URL,每次请求参数不同,$request_uri 就不同,哈希结果就不同——同一资源被散列到多台后端,本地缓存重复加载,命中率被严重稀释。
- 真实业务中,
?t=类时间戳参数占比常超 30%,不处理时哈希分散度可达 70% 以上 -
$uri虽能去掉查询参数,但会把/a/b和/a/b/视为不同路径,仍可能引入冗余 - 第三方
url_hash模块不可靠:非官方、不支持consistent、无法灵活过滤参数
推荐方案:用 map 预处理 + hash 标准化 URI
在 http 块中定义一个标准化的变量,剥离无关参数,保留真正影响资源内容的关键部分:
map $request_uri $cache_key {
~^(/[^?]*)(\?[^#]*)?$ $1;
default $request_uri;
}
然后在 upstream 中使用该变量哈希:
upstream static_pool {
hash $cache_key consistent;
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
- 这个
map正则提取路径部分(/img/logo.png),忽略全部查询参数,适配绝大多数静态资源场景 - 如需保留特定参数(如
?v=2.1表示版本),可升级正则:~^(/[^?]*)(\?v=[^&]+) - 务必加
consistent,节点扩缩容时仅影响约 1/N 的 key 映射,避免全量缓存失效
进阶:按资源类型分层哈希
图片、字体、JS/CSS 访问特征差异大,可拆成多个 upstream,各自定制哈希逻辑:
map $request_uri $img_key {
~^(/static/img/[^?]*)(\?.*)?$ $1;
default "";
}
map $request_uri $js_key {
~^(/static/js/[^?]*)(\?.*)?$ $1;
default "";
}
upstream img_servers { hash $img_key consistent; server ...; }
upstream js_servers { hash $js_key consistent; server ...; }
- 配合
location分流,让不同资源走专属集群,提升局部缓存密度 - 例如图片集群可配置更高内存缓存,JS 集群启用更强压缩,策略更精细
- 注意:
$img_key为空时,Nginx 会 fallback 到轮询,建议加default ""显式控制
验证与调优要点
光配对不等于生效,关键要确认三点:
- 日志中加入
$cache_key和$upstream_cache_status字段,观察相同资源是否始终打到同一$upstream_addr且HIT比例上升 - 用
curl -I检查后端返回的Cache-Control和ETag是否规范,否则 Nginx 代理缓存不会生效 - 若发现某类资源(如用户头像)URI 规则混乱,建议前端统一签名参数(如
?sig=abc123),比正则更稳定











