split_clients不能直接用$cookie_xxx作随机采样,因为空Cookie被统一哈希为固定值,导致所有无Cookie请求落入同一分组;必须用map预处理构造稳定非空键(如fallback到$binary_remote_addr),并配合add_header写入Cookie。

split_clients 不能直接用 $cookie_xxx 做“随机采样”,必须配合 fallback 逻辑和预处理,否则空 Cookie 会全部落入 default 分组,导致实际采样率严重失真。
为什么直接写 split_clients "$cookie_ab_sample" $sample_group 会失效
模块对未定义或空值的变量统一按空字符串 "" 处理,而 "" 的 MD5 哈希结果固定,取模后必然落在某个确定区间——这意味着所有无 Cookie 的请求(首次访问、隐私模式、禁用 Cookie 的客户端)都会被分到同一个分组,完全破坏“随机采样”意图。
- 常见错误现象:
5% "on"; * "off";配置下,实测采集率远高于 5%,甚至接近 100% - 根本原因:大量用户首次请求时
$cookie_ab_sample为空,全被哈希到同一 bucket - 真实场景中,移动端 WebView、iOS ITP、浏览器隐私模式下 Cookie 获取失败率常超 30%
正确做法:用 map + split_clients 构造稳定非空键
核心是把“是否已有采样标识”和“生成新标识”拆开,在变量进入 split_clients 前就完成标准化。推荐组合:
- 优先读
$cookie_ab_sample,有则直接使用 - 无则 fallback 到
$binary_remote_addr(IPv4/IPv6 二进制格式,哈希稳定性高) - 避免拼接
$http_user_agent:移动端 UA 变化频繁,会导致同一设备反复换组
配置示例:
map $cookie_ab_sample $ab_key {
"" $binary_remote_addr;
default $cookie_ab_sample;
}
<p>split_clients $ab_key $ab_sampled {
5% "true";</p><ul><li>"false";
}如何让新用户真正“被采样”并写回 Cookie
split_clients 本身不支持设置 Cookie,必须靠后续阶段补足。关键点是:分流结果要能用于条件判断,且写 Cookie 必须在响应头发出前完成。
- 用
if ($ab_sampled = "true") { add_header Set-Cookie "ab_sampled=true; Path=/; Max-Age=86400"; }—— 注意:add_header 在 location 中生效,但需确保该 location 不被 proxy_pass 覆盖 - 更可靠方式:在
server块顶层用map定义目标行为,再统一用proxy_set_header或add_header注入 - 绝对不要依赖
rand():它每次请求都变,无法保证同一用户多次请求一致性
容易被忽略的兼容性与调试陷阱
生产环境上线前必须验证三件事,缺一不可:
- 确认 Nginx 版本 ≥ 1.11.0(
split_clients模块默认启用;旧版需检查是否编译进内核) - 日志中加
$ab_sampled $cookie_ab_sample $binary_remote_addr字段,抽样比对空 Cookie 请求是否真的被 fallback 到 IP - 用 curl 测试:先
curl -I http://example.com看是否返回Set-Cookie,再带 Cookie 重试,确认二次请求$ab_sampled不变
最隐蔽的问题往往出在 CDN 或反向代理层:如果上游已剥离 Cookie 或伪造 X-Forwarded-For,$binary_remote_addr 就变成了 CDN 节点 IP,导致整个采样逻辑坍缩为“CDN 节点级分流”。务必确认真实客户端 IP 是否透传到位。


















