用 $http_user_agent 可实现按浏览器指纹稳定分流,但它是字符串哈希而非语义识别,相同 ua 总落同一后端,微小差异(如空格、大小写)即导致不同哈希结果;nginx 中仅 $http_user_agent 是合法变量,$user_agent 不存在。

直接用 hash $http_user_agent 可实现按浏览器指纹做稳定分流,但需注意它本质是“字符串哈希”,不是“语义识别”——它把整个 User-Agent 字符串当作一个不可拆解的键来计算哈希值,相同 UA 总落到同一后端,不同 UA(哪怕只差一个空格)就可能分到不同节点。
为什么用 $http_user_agent 而不是 $user_agent
Nginx 中只有 $http_user_agent 是标准内置变量,对应请求头中原始的 User-Agent 字段值;$user_agent 并不存在,属常见误写。该变量未解码、未归一化,保留大小写与特殊字符,例如:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/122.0-
mozilla/5.0 (windows nt 10.0; win64; x64) chrome/122.0→ 哈希结果不同 -
Mozilla/5.0 (X11; Linux x86_64) Chrome/122.0→ 视为完全不同的键
典型配置方式:一致性哈希 + 后端分组
在 upstream 块中启用带 consistent 的哈希,避免节点伸缩时大量映射漂移:
upstream browser_pool {
hash $http_user_agent consistent;
server 192.168.1.10:8080 weight=1;
server 192.168.1.11:8080 weight=1;
server 192.168.1.12:8080 weight=1;
}
再在 location 中调用:
location / {
proxy_pass http://browser_pool;
proxy_set_header Host $host;
}
这样 Chrome Windows 用户始终打到同一台后端,Safari macOS 用户固定另一台,适合会话粘滞弱依赖或前端资源缓存亲和场景。
慎用提醒:这不是“按浏览器类型分流”
hash $http_user_agent 不等于“Chrome 流量走 A 组,Firefox 走 B 组”。它不做模式匹配,不提取浏览器名或版本,只是对整串 UA 做哈希。若你真正需要的是:
- Chrome 用户访问
/chrome-optimized接口 - iOS Safari 用户返回精简版 JSON
- 爬虫 UA 直接拦截或限速
那应该用 if ($http_user_agent ~* Chrome) 或 map 指令分类,而不是 hash。
进阶建议:轻量归一化提升稳定性
真实环境中 UA 差异极多(如带 `Edg/`、`OPR/`、`wv`、`; wv` 等),可借助 map 提取关键标识再哈希:
map $http_user_agent $ua_family {
default "other";
~*Chrome "chrome";
~*Firefox "firefox";
~*Safari.*Version "safari";
~*Edg "edge";
~*iPhone|iPod|iPad "ios";
~*Android "android";
}
upstream ua_group {
hash $ua_family consistent;
server 10.0.2.1:8080;
server 10.0.2.2:8080;
}
此时所有 Chrome 变体(包括 Edge Chromium、QQ Browser)都归入 "chrome" 类,哈希更可控,也更贴近业务意图。











