Nginx基于请求头的分发依赖map指令构建路由变量(如$http_x_release_channel映射为$upstream_backend),再结合proxy_pass http://$upstream_backend实现动态转发;server块仅负责Host匹配,不直接处理请求头分发,需在http块中统一定义map并确保透传头、禁用缓存干扰及配置健康检查。

核心机制:map 构建路由变量
`map` 是 Nginx 实现条件映射的轻量高效方式,它在请求处理早期就完成变量赋值,后续可安全用于 `proxy_pass`、`if`(慎用)、`location` 匹配等。例如,按灰度标识头选择后端集群:
map $http_x_release_channel $upstream_backend {
default app-prod;
"canary" app-canary;
"internal" app-staging;
}
这个 `$upstream_backend` 变量可在 `server` 块的 `location` 中直接引用:
location / {
proxy_pass http://$upstream_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
常见请求头分发场景与配置要点
实际使用中需注意匹配逻辑、默认兜底和变量作用域:
- User-Agent 分流(PC/移动/平板):用 `~*` 忽略大小写匹配关键词,避免漏判;`default` 必须设,否则未匹配时变量为空导致 502
- 租户或 API 版本头(如 X-Tenant-ID、Accept):建议统一用小写 header 名(Nginx 自动转为小写),避免大小写敏感问题
- 地理区域分流(基于 X-Forwarded-For 或 GeoIP 模块):若用 `geoip2`,需先通过 `geoip2` 指令生成 `$geoip2_data_country_iso_code` 这类变量,再用 `map` 映射到集群名
- Cookie 或认证头(如 Authorization、X-User-ID):注意 Cookie 是客户端发送的原始字符串,可能含空格或特殊字符,`map` 匹配时建议用正则捕获更稳妥
为什么不能只靠 server 块?
`server` 块的 `server_name` 只匹配 Host 头,且匹配发生在连接建立初期,无法感知其他请求头内容。若强行在 `server` 块里用 `if` 判断请求头再改 `proxy_pass`,不仅性能差(`if` 在 location 内才安全)、易出错,还违反 Nginx 推荐的配置范式。
正确做法是:
- 把所有请求头判断逻辑收拢到 `http` 块的 `map` 中,集中维护
- 每个 `server` 块专注处理协议、证书、监听端口等网络层配置
- 把路由决策结果(如 `$upstream_backend`)作为“参数”传给通用的 `location /` 转发逻辑
必须配合的关键设置
仅靠 `map` 和 `proxy_pass` 不够,还需确保后端能正确识别原始意图:
- 透传原始请求头:`proxy_set_header X-Original-Host $host;`、`proxy_set_header X-Request-Header-Value $http_x_release_channel;`(便于后端审计或二次路由)
- 禁用缓存干扰:若分发逻辑依赖请求头,需加 `proxy_cache_bypass $http_x_release_channel;` 避免缓存覆盖不同策略的响应
- 健康检查与 fallback:`upstream` 中为各集群配置 `max_fails=2 fail_timeout=30s`,当 `canary` 集群不可用时,`map` 仍会指向它——需靠 upstream 自身熔断,而非 map 层兜底


















