nginx通过可信上游请求头(如cf-ipcountry)实现地理转发与透传,无需geoip2模块;需先验证头存在,再用proxy_set_header透传或map映射路由,提升精度与性能。

Linux 下 Nginx 实现“通过请求头转发 + 精准地理位置识别”,本质是两步协同:先从上游(如 CDN、WAF)可信请求头中提取地理字段,再将其透传或用于路由决策。它不依赖本地 GeoIP2 解析,而是复用已有的高质量地理信息,响应更快、精度更高、运维更轻。
确认上游是否已注入地理头(关键前提)
多数主流 CDN(Cloudflare、阿里云全站加速、腾讯云 CDN、AWS CloudFront)会在请求到达源站前,自动添加标准地理头,例如:
- Cloudflare:CF-IPCountry、CF-IPCity、CF-IPRegion、CF-IPLat、CF-IPLon
- 阿里云 CDN:X-Cdn-Geoip-Country、X-Cdn-Geoip-City
- 腾讯云 CDN:X-Tencent-Geoip-Country、X-Tencent-Geoip-City
可在 Nginx 中加一个测试 location 验证是否收到:
Linux系统管理专家,覆盖12大模块:用户权限、SSH、存储、网络、systemd、防火墙、日志监控、备份恢复、TLS证书、Ansible、容器、IaC。提供配置、验证、加固、监控、备份、自动化、故障排查、回滚闭环。关键词:useradd、sudo、sshd_config、chmod、SEL...
用 proxy_set_header 透传地理信息给后端
在对应 location 块中,直接映射请求头为自定义代理头,供后端业务使用:
- $http_cf_ipcountry 是 Nginx 内置变量,自动转小写并读取 CF-IPCountry 头值
- 所有 $http_* 变量名均为小写格式(CF-IPCountry → $http_cf_ipcountry)
- 建议统一前缀避免冲突,例如 X-Geo-Country、X-Geo-City
proxy_pass http://backend;
proxy_set_header X-Geo-Country $http_cf_ipcountry;
proxy_set_header X-Geo-City $http_cf_ipcity;
proxy_set_header X-Geo-Region $http_cf_ipregion;
proxy_set_header X-Real-IP $remote_addr;
}
用请求头值做条件转发(非 geoip2 模块)
若需根据地理头直接路由(比如 country=CN 走国内集群),可用 if + proxy_pass(注意:仅限简单单层判断,不推荐嵌套复杂逻辑):
- if 在 location 内可用,但不能嵌套在其他 if 或 map 中
- 务必配合 rewrite break 清理路径,防止重复拼接
- 更稳定的做法是:用 map 预定义变量(见下条),再 proxy_pass 引用
if ($http_cf_ipcountry = "CN") {
proxy_pass http://cn-upstream;
rewrite ^/service/(.*) /$1 break;
}
if ($http_cf_ipcountry = "US") {
proxy_pass http://us-upstream;
rewrite ^/service/(.*) /$1 break;
}
proxy_pass http://default-upstream;
}
进阶:用 map + 请求头实现无 if 的静态路由映射
map 必须定义在 http 块顶层,它把请求头值(字符串)映射为 upstream 名,再由 proxy_pass 引用——性能高、语法清晰、规避 if 的限制:
- 源变量必须是字符串型,$http_cf_ipcountry 符合要求
- 匹配严格区分大小写,CN ≠ cn;无空格、无换行
- default 分支兜底,避免未命中导致 502
map $http_cf_ipcountry $geo_upstream {
default "default-backend";
CN "cn-backend";
US "us-backend";
JP "jp-backend";
}
upstream cn-backend { server 10.0.1.10:8080; }
upstream us-backend { server 10.0.2.20:8080; }
upstream jp-backend { server 10.0.3.30:8080; }
upstream default-backend { server 10.0.0.100:8080; }
server {
location / {
proxy_pass http://$geo_upstream;
proxy_set_header Host $host;
}
}
}










