GeoIP2模块仅支持七层HTTP场景,无法用于四层TCP/UDP代理——因其仅注册在http上下文,stream块中不可用且不解析IP地理位置;可行方案包括:①stream+Lua查MaxMindDB;②前置HTTP网关做GeoIP2分流;③LVS+ipset基于IP段调度。

GeoIP2 模块本身工作在七层(HTTP 层),无法直接用于四层(TCP/UDP)代理场景。Nginx 的 ngx_http_geoip2_module 仅支持 http 块下的变量注入与逻辑判断,不参与 stream 块的流量处理。因此,“在四层代理层面利用 GeoIP2 模块实现地理位置调度”在技术上不可行——这不是配置问题,而是模块设计边界决定的。
为什么四层无法用 GeoIP2
Nginx 的 stream 模块处理的是原始 TCP/UDP 流,不解析 HTTP 头、不识别 URI、也不触发 geoip2 指令所需的 JSON 路径解析能力。即使你编译了 geoip2 模块,它也只注册在 http 上下文,stream { ... } 中无法声明 geoip2 指令,相关变量(如 $geoip2_data_country_code)根本不可用。
可行的替代方案
若业务必须在四层做地理调度,需绕过 GeoIP2,采用以下实际可用路径:
-
用 real_ip + 四层 geoip 查表:在
stream块中通过set_real_ip_from和real_ip_header获取真实客户端 IP 后,调用外部 Lua 脚本(需 OpenResty +lua-resty-maxminddb)或本地 C 模块查 GeoLite2 数据库,再基于结果选择 upstream。这是目前最主流的四层地理路由实践。 -
前置七层网关兜底:将所有流量先经由一个轻量级 HTTP 入口(哪怕只是 307 临时重定向),在此完成 GeoIP2 解析与
map分流,再用proxy_pass转发到对应四层后端。适用于 TLS 终结可接受的场景。 -
用 LVS + ipset + 地理 IP 段聚合:从 MaxMind 下载 CSV 格式国家 IP 段(如 GeoLite2-Country-Blocks-IPv4.csv),按国家提取 CIDR 列表,导入 Linux
ipset,再结合iptables或ipvsadm实现基于目标 IP 段的 DNAT 调度。无需 Nginx,纯内核态,延迟最低,但粒度限于国家/大区,且需定期更新脚本。
特别注意
不要尝试在 stream 块里写 geoip2 /path/to/mmdb { ... } —— Nginx 启动会报错“unknown directive”。也不要依赖 CDN 回源头在 stream 层伪造地理信息,这极易被篡改且缺乏可信链。真实地理调度必须基于客户端出口 IP 的权威数据库匹配,且应在尽可能早的可信环节完成。

















