Nginx 实现基于 Cookie 的灰度路由应使用 http 块中的 map 指令提取 Cookie 值映射为变量,再通过 proxy_pass 动态路由至对应 upstream;严禁在 server 或 location 中用 if + set 分流,避免 502 等风险。

Nginx 在 Virtual Host(即 server 块)中实现基于 Cookie 的灰度路由,不能直接在 server 或 location 内用 if + set 做核心分流——这是官方明确不推荐的高危写法,容易引发 502、变量未定义、逻辑错乱等问题。正确做法是:把分流逻辑前置到 http 块,用 map 提前提取并映射 Cookie 值为变量,再在 server 中干净调用。
以下为可落地、生产可用的配置结构:
✅ 正确写法:map + upstream + proxy_pass 动态路由
# 1. 在 http 块顶层(不能放在 server 或 location 里)定义 map
http {
# 从 $http_cookie 字符串中正则匹配 gray= 后的值(忽略大小写)
map $http_cookie $upstream_backend {
default "backend-stable"; # 默认走旧版
~*gray=v2 "backend-canary"; # 匹配 gray=v2
~*gray=canary "backend-canary";
~*uid=abc123 "backend-canary"; # 可扩展:按用户 ID 灰度
}
# 2. 定义 upstream(必须提前声明,不能动态拼接)
upstream backend-stable {
server 192.168.1.10:8080 max_fails=2 fail_timeout=30s;
}
upstream backend-canary {
server 192.168.1.11:8080 max_fails=2 fail_timeout=30s;
}
# 3. server 块中直接使用已映射变量
server {
listen 80;
server_name example.com;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 关键:proxy_pass 后跟变量名,对应 upstream 名
proxy_pass http://$upstream_backend;
# 可选:透传灰度标识给后端
proxy_set_header X-Gray-Version $upstream_backend;
}
}
}
⚠️ 注意事项(容易出错的关键点)
-
map必须写在http { }最外层,不能嵌套在server或location里; -
$http_cookie是原始 Cookie 字符串(如gray=v2; user_id=abc; path=/),Nginx 不自动解析成$cookie_gray,除非你启用了underscores_in_headers on且 Cookie 名不含特殊字符; - 正则必须加
~*(忽略大小写),否则Gray=v2就匹配不上; -
default分支必不可少,否则未匹配时$upstream_backend为空,proxy_pass http://后跟空值会直接 502; -
upstream名必须与map中字符串完全一致,且只能是已定义的块名,不支持运行时拼接(如proxy_pass http://backend_$env是非法的); - 测试时用
curl -H "Cookie: gray=v2" http://example.com/,别依赖浏览器——浏览器可能带多余 Cookie 或缓存旧值。
? 补充技巧:让灰度更可控
- 若需对首次访问用户自动打标(比如 AB 测试),需配合后端或 OpenResty 的 Lua 脚本设置 Cookie,Nginx 自身无法生成新 Cookie;
- 想让灰度用户始终走同一版本(会话粘性),不要混用
ip_hash或hash $cookie_xxx,它们会覆盖map的分流逻辑; - 后端服务可通过
X-Gray-VersionHeader 判断流量归属,便于日志标记、功能开关或数据隔离。
基本上就这些。配置简洁,逻辑清晰,也经得起高并发验证。











