灯下哥谭 灯下哥谭
首页
关于
  • Hermes Agent 平台
  • Claude Code
  • OpenClaw
  • GPU 推理节点运维
  • DeepSeek Harness
  • MySQL 运维知识地图
  • Elasticsearch 运维知识地图
  • Redis 运维知识地图
  • TiDB 体系
  • DBA 常用 SQL 与命令
  • Nginx 运维知识地图
  • Prometheus 监控
  • Docker
  • Systemd
  • Iptables
  • Firewalld
  • Sshd
  • MySQL8 运维 SOP 手册
  • MySQL 实战 45 讲(读书笔记)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

灯下哥谭

灯还亮着
首页
关于
  • Hermes Agent 平台
  • Claude Code
  • OpenClaw
  • GPU 推理节点运维
  • DeepSeek Harness
  • MySQL 运维知识地图
  • Elasticsearch 运维知识地图
  • Redis 运维知识地图
  • TiDB 体系
  • DBA 常用 SQL 与命令
  • Nginx 运维知识地图
  • Prometheus 监控
  • Docker
  • Systemd
  • Iptables
  • Firewalld
  • Sshd
  • MySQL8 运维 SOP 手册
  • MySQL 实战 45 讲(读书笔记)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 工作笔记

  • 容器与编排

  • Nginx

    • Nginx 运维知识地图:从配置基础到反向代理实战
    • Nginx 给内部服务加认证:Basic Auth / LDAP / OIDC 三档怎么选
    • 利用nginx+sftp实现一个可供用户下载的服务
    • nginx配置文件及模块
    • Nginx 端口转发与复用:四层代理与 SO_REUSEPORT 实战
    • OpenResty 版本升级与模块管理:从 1.13 到 1.19 的实践
    • NGINX基于cookie针对同一域名进行分流转发
      • 1. 背景与原理
        • 1.1 工作原理
        • 1.2 典型应用场景
      • 2,环境准备。
      • 3,思路说明。
      • 4,开始操作。
        • 1,先启两个后端容器。
        • 2,再启动一个前端 NGINX。
        • 3,访问测试。
      • 5,其他方面。
        • 1,匹配结尾关键字。
        • 2,匹配开头关键字。
        • 3,匹配包含关键字。
      • 6. 坑与边界
      • 7. 进阶:基于 Header 的分流
      • 8. 参考
      • 9. 生产环境配置示例
      • 10. 性能影响
      • 11. 与其他分流方案的对比
      • 12. 常见错误排查
      • 13. 参考
    • nginx利用内置模块配置限速限流
    • 利用NGINX内置模块mirror进行流量复制等操作
    • Nginx 常用配置速查
    • Nginx 日志切割两种方案对比:外部脚本 vs 配置文件原生切割
    • nginx配置微信小程序校验及其他
    • 由Nginx集中代理分散的PHP集群的实践
    • http状态码详解
    • 排查NGINX的open_file_cache导致发布后访问404的问题
    • Nginx Proxy Manager 使用笔记
  • 监控

  • 网络安全

  • 其他

  • Linux笔记
  • Nginx
灯下哥谭
2019-08-03
目录

NGINX基于cookie针对同一域名进行分流转发

最新了解到的姿势,结合着新接触 Mac 电脑,第一次做实验,学习之后,特别记录一下。

版本说明

本文写于 2019-08。
未在更高版本上重新验证,请以对应版本的官方文档为准。

# 1. 背景与原理

有时候测试环境会有多套不同的服务(比如 dev、staging、preprod),如果每套都配置独立的域名,客户端需要频繁切换域名,管理成本很高。更优雅的方案是在 NGINX 层面根据请求中的 cookie 信息,将同一域名的流量分发到不同的后端服务。

# 1.1 工作原理

Nginx 的 map 指令可以从变量中提取值并映射到新的变量。结合 $COOKIE_xxx 变量,可以根据客户端携带的 cookie 动态选择上游服务器。

这种方案的优势在于:

  • 无需修改客户端代码:浏览器自动发送 cookie,Nginx 根据 cookie 路由
  • 无感知切换:用户只需要在测试工具中设置不同的 cookie 值,即可切换环境
  • 配置灵活:支持精确匹配、前缀匹配、后缀匹配、正则匹配等多种方式

# 1.2 典型应用场景

A/B 测试:根据用户 cookie 将流量分配到不同版本的服务,验证新功能的效果。

多环境调试:开发人员通过设置特定的 cookie 值,访问测试环境、预发布环境,而无需修改 hosts 文件或使用独立的域名。

灰度发布:新版本上线时,可以先将少量用户的流量导入新服务,观察运行情况后再逐步扩大范围。

很多时候,测试环境可能会有好多套环境,这个时候,如果每套都配置一个对应的域名,会非常麻烦,但是很多时候针对这个问题似乎又没有特别好的方案,新公司新气象,学到新的思路是在 NGINX 层面基于 cookie 来进行不同环境的分流转发,今天就来做一下这个实验。

# 2,环境准备。

因为在新环境,还没有个人自用的测试服务器,Mac 当中做实验又不习惯,于是只能通过 docker 来进行了。

所以需要先安装 docker 环境,这个就不在这里赘述了。

那么,docker 环境准备完毕之后,就可以开始实验了,所谓,docker 在手,天下我有。

img

# 3,思路说明。

首先跑两个 NGINX 的容器,访问之后会返回不同的结果,然后前端再添加一层 NGINX,代理所有的外部请求,根据 cookie 的不同,分发到不同的后端容器去。

# 4,开始操作。

# 1,先启两个后端容器。

准备工作:

$ mkdir -p /Users/liqilong/docker/nginx
$ cd /Users/liqilong/docker/nginx
$ mkdir  test1 test2
$ echo test1 > test1/index.html
$ echo test2 > test2/index.html
1
2
3
4
5

启动容器:

$ docker pull daocloud.io/library/nginx:1.15.9-alpine-perl
$ docker run --name test1 -v /Users/liqilong/docker/nginx/test1:/usr/share/nginx/html:ro -d -p 8080:80  daocloud.io/library/nginx:1.15.9-alpine-perl
$ docker run --name test2 -v /Users/liqilong/docker/nginx/test2:/usr/share/nginx/html:ro -d -p 8081:80  daocloud.io/library/nginx:1.15.9-alpine-perl
1
2
3

访问验证:

$ ifconfig en0
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    ether 02:00:00:00:00:01
    inet6 fe80::1cf4:9734:2fa8:8234%en0 prefixlen 64 secured scopeid 0x5
    inet 192.0.2.11 netmask 0xfffffc00 broadcast 192.0.2.12
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect
    status: active
$ curl 192.0.2.11:8080
test1
$ curl 192.0.2.11:8081
test2
1
2
3
4
5
6
7
8
9
10
11
12

# 2,再启动一个前端 NGINX。

因为要做一些相对的配置工作,我这里就用了自己配置的 centos 镜像来做了,事实上仍旧可以利用刚刚那个 NGINX 镜像来做接下来的实验。

$ docker pull registry.cn-hangzhou.aliyuncs.com/eryajf/centos:7.4
$ docker run -itd --name eryajf registry.cn-hangzhou.aliyuncs.com/eryajf/centos:7.4
1
2

接下来的操作就是进入此容器内部进行了。

$ docker exec -it eryajf sh
sh-4.2# yum localinstall http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
sh-4.2# yum -y install nginx
1
2
3

添加如下 NGINX 配置:

cat >> /etc/nginx/conf.d/test.conf << EOF
upstream test01 {       #此处可以单独写,也可以写在下边map的内容中
    server 192.0.2.11:8080 weight=1 max_fails=1 fail_timeout=30s;
}
upstream test02 {
    server 192.0.2.11:8081 weight=1 max_fails=1 fail_timeout=30s;
}
upstream root {
    server 192.0.2.11:8080 weight=1 max_fails=1 fail_timeout=30s;
}
map $COOKIE_testenv $group {    #$COOKIE_testenv的前半部分$COOKIE_是固定格式,后边的testenv则是cookie的key,$group是别名
    test1 test01;   #表示cookie的value=test1,则转发给test1
    test2 test02;
    default root;
}
server {
    listen 81;
    server_name localhost;
    access_log  logs/access_log;
    error_log   logs/error_log;
    location / {
        proxy_pass http://$group$request_uri;   #注意此处url的拼接
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}
EOF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

然后启动 NGINX:

nginx -t
nginx
1
2

# 3,访问测试。

这个时候可以通过命令行来模拟请求,然后查看效果。

sh-4.2# curl localhost:81
test1
sh-4.2# curl localhost:81 --cookie "testenv=test1"
test1
sh-4.2# curl localhost:81 --cookie "testenv=test2"
test2
1
2
3
4
5
6

此处只要是有一个 cookie 名称与内容是符合 nginx 定义的规则的,那么如上规则就是成立的。

sh-4.2# curl localhost:81 --cookie "testenv=test1;user=root;pass=123"
test1
sh-4.2# curl localhost:81 --cookie "testenv=test2;user=root;pass=123"
test2
1
2
3
4

# 5,其他方面。

另外除了上边的比较固定的方式之外,还有比较灵活的控制方案,主要集中在 url 的匹配上。

# 1,匹配结尾关键字。

需求就是匹配到 cookie 的指定结尾进行分流转发。NGINX 配置如下:

map $COOKIE_testenv $group {
    ~*1$  192.0.2.11:8080;
    ~*2$  192.0.2.11:8081;
    default 192.0.2.11:8080;
}
server {
    listen 81;
    server_name localhost;
    access_log  logs/access_log;
    error_log   logs/error_log;
    location / {
        proxy_pass http://$group$request_uri;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

然后重新加载 NGINX 配置,请求一下验证效果:

sh-4.2# curl localhost:81 --cookie "testenv=dfhg;user=root;pass=123"
test1
sh-4.2# curl localhost:81 --cookie "testenv=dfhg1;user=root;pass=123"
test1
sh-4.2# curl localhost:81 --cookie "testenv=dfhg2;user=root;pass=123"
test2
1
2
3
4
5
6

# 2,匹配开头关键字。

与上边的道理是一致的,只不过配置内容更改一下即可。

map $COOKIE_testenv $group {
    ~*^1  192.0.2.11:8080;
    ~*^2  192.0.2.11:8081;
    default 192.0.2.11:8080;
}
server {
    listen 81;
    server_name localhost;
    access_log  logs/access_log;
    error_log   logs/error_log;
    location / {
        proxy_pass http://$group$request_uri;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

然后请求一下,验证一下效果:

sh-4.2# curl localhost:81 --cookie "testenv=dfhg;user=root;pass=123"
test1
sh-4.2# curl localhost:81 --cookie "testenv=1dfhg;user=root;pass=123"
test1
sh-4.2# curl localhost:81 --cookie "testenv=2dfhg;user=root;pass=123"
test2
1
2
3
4
5
6

# 3,匹配包含关键字。

还有一种比较灵活的策略,就是只要包含指定的关键字标识,就往不同的后端进行分流转发,配置如下:

map $COOKIE_testenv $group {
    ~*.*eryajf1.*  192.0.2.11:8080;
    ~*.*eryajf2.*  192.0.2.11:8081;
    default 192.0.2.11:8080;
}
server {
    listen 81;
    server_name localhost;
    access_log  logs/access_log;
    error_log   logs/error_log;
    location / {
        proxy_pass http://$group$request_uri;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

然后请求一下,验证一下效果:

sh-4.2# curl localhost:81 --cookie "testenv=A3fklj;user=root;pass=123"
test1
sh-4.2# curl localhost:81 --cookie "testenv=A3fkeryajf1lj;user=root;pass=123"
test1
sh-4.2# curl localhost:81 --cookie "testenv=A3fkeryajf2lj;user=root;pass=123"
test2
1
2
3
4
5
6

# 6. 坑与边界

常见问题一:Cookie 未携带

如果客户端未携带任何 cookie,将使用 default 分组。确保 default 分组指向正确的后端服务。

常见问题二:正则匹配性能

正则匹配比精确匹配性能稍差,如果匹配规则简单,建议使用精确匹配。

常见问题三:大小写敏感

默认情况下 map 匹配是区分大小写的,如果需要不区分大小写,可以使用 ~* 前缀。

# 7. 进阶:基于 Header 的分流

除了 Cookie,还可以基于请求 Header 进行分流:

map $http_x_env $group {
    dev 192.0.2.11:8080;
    staging 192.0.2.11:8081;
    prod 192.0.2.11:8082;
    default 192.0.2.11:8080;
}
1
2
3
4
5
6

使用方式:

curl -H "X-Env: staging" http://example.com/
1

# 8. 参考

  • Nginx map 模块官方文档 (opens new window)

# 9. 生产环境配置示例

在生产环境中,Cookie 分流通常与其他机制配合使用:

场景:AB 测试

http {
    # 从 Cookie 中提取用户分组
    map $COOKIE_ab_group $backend {
        "A" "backend_a";
        "B" "backend_b";
        default "backend_default";
    }
    
    upstream backend_a {
        server 192.0.2.21:8080;
    }
    upstream backend_b {
        server 192.0.2.22:8080;
    }
    upstream backend_default {
        server 192.0.2.20:8080;
    }
    
    server {
        listen 80;
        server_name example.com;
        
        location / {
            proxy_pass http://$backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

配置说明:

  • 用户首次访问时,后端服务设置 ab_group Cookie(通过代码或 Nginx 脚本)
  • 后续请求携带该 Cookie,Nginx 根据值路由到不同后端
  • 可以实现不同用户看到不同版本的页面

# 10. 性能影响

Cookie 分流的性能开销极低,原因如下:

  1. 只读取请求头:Cookie 已在 HTTP 请求中,无需额外查询
  2. 内存操作:map 指令的执行是纯内存操作,速度在纳秒级
  3. 无状态:不需要维护会话状态,Nginx 本身无状态

相比其他分流方式(如 DNS 轮询、负载均衡器),Cookie 分流的优势是实现简单且生效快。

# 11. 与其他分流方案的对比

方案 优点 缺点 适用场景
Cookie 分流 无需修改客户端,实现简单 依赖 Cookie A/B 测试、多环境切换
URL 参数分流 直观可见 URL 需要变更 调试场景
IP 哈希分流 固定用户固定后端 无法灵活控制 简单灰度
DNS 分流 域名级别控制 生效慢 大规模流量切换

Cookie 分流是最灵活且对用户透明的方案,推荐作为首选。

# 12. 常见错误排查

错误一:分流不生效

检查 map 块是否在 http 层定义,变量名是否与实际使用的 Cookie 名称一致。

错误二:循环重定向

确保 upstream 名称与 server 内的 proxy_pass 名称不重复。


# 13. 参考

  • Nginx map 模块官方文档 (opens new window)
  • Nginx upstream 模块文档 (opens new window)
#架构设计#Nginx
上次更新: 9/11/2026

← OpenResty 版本升级与模块管理:从 1.13 到 1.19 的实践 nginx利用内置模块配置限速限流→

最近更新
01
DeepSeek Harness 实战 06|学习笔记:插件、工具、技能不在同一个维度上 原创
09-11
02
DeepSeek Harness 实战 05|让两个编码 Agent 共用一份长期记忆 原创
09-09
03
DeepSeek Harness 实战 04|学习笔记:从「已知限制」里读出三处设计张力 原创
09-08
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式