nginx 可通过 map 指令结合 geoip2 数据库实现轻量高效地域分流:先配置 geoip2 模块加载 ip 地理数据,再用 map 将国家码映射为日志路径或 upstream 标签,最后在 access_log 和 proxy_pass 中引用;需设 default 分支、预建目录并注意地理数据固有误差。

直接用 Nginx 的 map 指令结合 IP 地理位置数据,可以实现轻量、高效、无外部依赖的日志分发与路径动态生成。关键不在于“查库”或调用 API,而是在请求进入时,基于已知的 IP 归属信息(如 GeoIP2 数据库)快速映射出地域标识,再驱动后续行为。
准备 IP 地理位置映射数据源
Nginx 本身不内置地理定位能力,需先加载结构化地理数据。推荐使用 MaxMind GeoIP2 的二进制数据库(GeoLite2-Country.mmdb 或更细粒度的 City 版本),通过 ngx_http_geoip2_module 模块读取。安装后,在 http 块中配置:
- 指定数据库路径:
geoip2 /path/to/GeoLite2-City.mmdb { $geoip2_data_country_code source=$remote_addr country iso_code; $geoip2_data_city_name source=$remote_addr city names en; }; - 确保模块已编译启用(常见于 OpenResty 或自定义编译的 Nginx)
- 若无法使用 GeoIP2,可用静态 IP 段映射表(如 CIDR 到 region 的 CSV),转为
geo指令配合map使用,但精度和维护成本较高
用 map 指令构建地域维度变量
map 的核心作用是将输入值(如国家码、城市名)转换为预定义的逻辑标签或路径片段。它在配置加载时编译,运行时零开销:
- 按国家分流日志目标:
map $geoip2_data_country_code $log_target { default "global"; CN "beijing"; US "ashburn"; JP "tokyo"; DE "frankfurt"; } - 构造动态日志路径:
map $geoip2_data_country_code $log_path { default "/var/log/nginx/access.log"; CN "/var/log/nginx/cn/access_$(date +%Y%m%d).log"; US "/var/log/nginx/us/access_$(date +%Y%m%d).log"; }(注意:Nginx 不支持 shell 变量,实际需用log_format中的$time_iso8601等内置变量组合) - 更实用的写法是拼接目录名:
map $geoip2_data_country_code $log_dir { default "global"; CN "cn"; US "us"; ~^[A-Z]{2}$ $geoip2_data_country_code; },再在access_log中引用/var/log/nginx/$log_dir/access.log
绑定到日志与代理行为
定义好映射变量后,即可在具体上下文中使用:
- 日志分发:
access_log /var/log/nginx/$log_dir/access.log main buffer=64k flush=5s;—— 每个地域写入独立目录,便于归档与审计 - 动态 upstream 选择:
upstream backend_$log_target { server 10.0.1.10:8080; server 10.0.1.11:8080; },再用proxy_pass http://backend_$log_target;实现就近路由 - 避免在
map中做复杂判断;如需多级归属(如“CN → beijing → chaoyang”),建议用嵌套map或提前在 GeoIP2 数据库中导出聚合字段
注意事项与边界情况
该方案简洁,但有明确适用前提:
- IP 地理位置数据存在固有误差,尤其对 CDN 回源、NAT 后用户、移动基站出口等场景,不宜用于强安全策略
-
map只接受字符串输入,若$geoip2_data_country_code为空(如私网 IP、未命中数据库),必须设default分支,否则变量为未定义,可能导致配置失败 - 日志路径中的目录需由运维提前创建并授权 Nginx worker 进程可写,否则会静默丢弃日志
- 不适用于需要实时更新地理位置(如设备移动中切换基站)的场景——Nginx 在连接建立时即完成映射,整个请求生命周期内不变











