前端页面已经可用,但后端又希望直接借现成页面来调试接口时,最麻烦的往往不是页面本身,而是如何把同一套请求稳定地切到不同开发环境。本文围绕这个问题,整理了两种基于 Nginx 的转发方案:一种用 map + upstream 快速按请求头分流,另一种用 Lua 做更灵活的动态路由;同时也补上浏览器侧改请求头和临时 mock 响应的配套方法,方便你判断该选哪一种。
为什么需要用请求头切换后端
这个需求通常出现在开发或联调阶段:前端页面已经写好,但后端不想手动组装复杂请求,也不想单独造一套调试数据,于是希望直接打开现成页面,通过真实操作去打本地接口。
这时,一个实用思路就是在请求里加一个自定义请求头,例如 gv,再由 Nginx 按这个值把流量转发到不同后端。这样前端代码本身不需要改动,同一套页面就能切不同接口环境。
浏览器侧怎么配合调试
用 ModHeader 改请求头
文中推荐使用 ModHeader。它的作用很直接:给浏览器发出的 HTTP 请求追加或修改请求头,比如加入 gv: zhangsan,让 Nginx 能识别当前请求该走哪个后端。
用 tweak 临时改响应
tweak 适合在后端接口偶发报错、字段不完整,或者某个流程只差一点数据就能走通时使用。它可以修改 HTTP 响应内容,临时 mock 数据,让前端流程继续执行。
这类工具并不替代正式联调,但在排查问题、验证页面逻辑时很省时间。
方案一:用 map + upstream 做固定规则分流
如果你的规则比较简单,比如只需要根据请求头 gv 的值,把请求切到固定几个后端,那么 map + upstream 是最直接的方案。

它的逻辑是:
- 如果没有传
gv,走默认分组; - 如果
gv是zhangsan,就走backend2; - 其他值找不到匹配时,仍然回到默认分组。
default.conf 配置示例
map $http_gv $backend {
default backend1;
zhangsan backend2;
}
upstream backend1 {
server 192.168.137.2:5021;
}
upstream backend2 {
server 192.168.137.2:5022;
}
server {
listen 8092;
location / {
proxy_pass http://$backend;
}
}
这里要注意两点:
$http_gv对应的是请求头里的gv;proxy_pass http://$backend;最终会把流量转发到map选中的 upstream。
如果你只维护少量固定环境,这种写法已经够用,而且配置清晰、排查也方便。
docker-compose.yml 示例
这里的镜像可以使用常见的 Nginx 镜像,原文示例如下:
version: '3.1'
services:
nginx-lua-gv:
image: huzhihui/nginx:1.23.1-lua
container_name: nginx-lua-gv
networks:
- default
environment:
- TZ=Asia/Shanghai
ports:
- 8092:8092
volumes:
- ./conf.d:/etc/nginx/conf.d
- ./logs:/var/log/nginx
networks:
default:
external:
name: huzhihui
其中暴露端口为 8092,并把本地 ./conf.d 挂载到容器内的 /etc/nginx/conf.d。
方案二:用 Lua 脚本做更灵活的动态转发
当后端环境更多,或者分流规则不再是简单的一对一映射时,Lua 方案会更合适。它的优势在于:你可以直接在脚本里写判断逻辑,而不是把所有情况都堆进静态配置。

代价也很明确:需要提前准备带 Lua 模块的 Nginx,配置复杂度比 map + upstream 更高。
Docker 镜像说明
原文提到,这里可以使用作者封装好的镜像,并且整体思路遵循官方指南。关键点不在镜像名字本身,而在于这个 Nginx 需要已经具备 Lua 相关模块。
nginx.conf 配置示例
这份配置的重点是先加载第三方模块,再在 location 中用 set_by_lua_block 动态决定 $backend:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
load_module modules/ndk_http_module.so;
load_module modules/ngx_http_lua_module.so;
load_module modules/ngx_stream_lua_module.so;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
server {
listen 8092;
location / {
set_by_lua_block $backend {
local gv = ngx.req.get_headers()["gv"]
local backend
if gv == "zhangsan" then
backend = "http://192.168.137.2:5022"
elseif gv == "lisi" then
backend = "http://192.168.137.2:5023"
else
backend = "http://192.168.137.2:5021"
end
return backend
}
proxy_pass $backend;
}
}
}
这段配置说明了几个实际区别:
gv=zhangsan时转到192.168.137.2:5022;gv=lisi时转到192.168.137.2:5023;- 未命中时默认走
192.168.137.2:5021; - 与 upstream 名称映射不同,这里可以直接返回完整 URL,扩展空间更大。
docker-compose.yml 示例
version: '3.1'
services:
nginx-lua-gv:
image: huzhihui/nginx:1.23.1-lua
container_name: nginx-lua-gv
networks:
- default
environment:
- TZ=Asia/Shanghai
ports:
- 8092:8092
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./logs:/var/log/nginx
networks:
default:
external:
name: huzhihui
和前一个方案相比,这里挂载的是完整的 nginx.conf,因为 Lua 模块加载和路由逻辑都写在主配置文件中。
两种方案怎么选
如果只是少量环境、规则固定,优先考虑 map + upstream。它足够直观,维护成本低,也更接近常规 Nginx 配置方式。

如果你需要:
- 按多个请求头联合判断;
- 针对不同用户、分支或接口做更细粒度路由;
- 后续还可能扩展更多动态逻辑;
那么 Lua 方案更灵活,但前提是你能接受更高的部署和维护复杂度。
测试结果说明
原文给出的验证结论很明确:
- 不传请求头时,会命中默认环境;
- 传入
zhangsan请求头时,请求会切到指定环境。
这也说明整套方案的核心并不在前端改代码,而在于浏览器追加请求头、Nginx 读取请求头并完成分流这两个环节是否打通。
如果你的目标只是让同一套前端页面快速服务多个开发后端,这种做法已经足够实用。







