OpenResty可通过Lua脚本结合GeoIP2等地理库实现精准地域分发:先用real_ip模块还原真实客户端IP,再通过lua-resty-maxminddb在access_by_lua_block中查询国家/城市等信息,动态设置upstream、重写URI或注入Header,全程高性能可控。

OpenResty 可以通过 Lua 脚本结合 IP 地理库(如 GeoIP2 或纯 Lua 实现的轻量库)获取客户端真实地理位置,并据此做路由、重写、Header 注入或 upstream 选择,实现精准内容分发。关键在于:拿到可信的客户端 IP、加载地理数据、在合适阶段(如 access_by_lua*)执行判断逻辑。
获取真实客户端 IP
用户可能经过 CDN 或反向代理,直接用 $remote_addr 会得到上一跳地址。需从可信 Header(如 X-Forwarded-For 或 CDN 提供的 X-Real-IP)提取原始 IP,并校验来源是否可信:
- 在
http块中配置set_real_ip_from指定可信代理网段 - 启用
real_ip_header X-Forwarded-For或real_ip_header X-Real-IP - 在 Lua 中用
ngx.var.remote_addr即可拿到真实客户端 IP(OpenResty 自动处理)
集成地理信息库(推荐 lua-resty-maxminddb)
使用 lua-resty-maxminddb(支持 MaxMind GeoLite2 City/ASN 数据库),比旧版 geoip 模块更准、更灵活:
- 下载 GeoLite2-City.mmdb(免费版需注册 MaxMind 账号)
- 将文件放到 OpenResty 的
conf/或data/目录下 - 在
http块中初始化:lua_shared_dict geo_db 10m; init_by_lua_block { local mmdb = require "resty.maxminddb" local ok, err = mmdb:new("/usr/local/openresty/conf/GeoLite2-City.mmdb") if not ok then ngx.log(ngx.ERR, "failed to initialize maxminddb: ", err) return end package.loaded["geo.mmdb"] = mmdb }
在 access 阶段做地域判断与分发
利用 access_by_lua_block 获取 IP 对应的国家、城市、时区等字段,再按需设置变量、跳转或选择 upstream:
- 查库示例:
local mmdb = require "geo.mmdb" local ip = ngx.var.remote_addr local res, err = mmdb:lookup(ip) if res and res.country and res.country.iso_code == "CN" then ngx.var.geo_country = "CN" ngx.var.upstream_group = "backend_cn" elseif res and res.country and res.country.iso_code == "US" then ngx.var.geo_country = "US" ngx.var.upstream_group = "backend_us" else ngx.var.geo_country = "OTHER" ngx.var.upstream_group = "backend_global" end - 后续可在
proxy_pass http://$upstream_group中动态路由 - 也可用
ngx.exec()重写 URI(如/cn/home→/home),或ngx.header.X-Geo透传地域信息给后端
优化与注意事项
生产环境需兼顾性能与准确性:
- 数据库文件建议用
mmdb内存映射方式加载,避免每次 lookup 读磁盘 - 对高频访问 IP 可加简单缓存(如
lua_shared_dict存 IP→country 映射,TTL 设 1 小时) - 务必处理
nil或查询失败情况,避免 Lua 报错中断请求 - 移动端或使用 Anycast 的 CDN 可能导致 IP 归属地不准,可结合 UA、语言头(
Accept-Language)辅助判断 - 注意 GDPR / 个人信息合规,不记录或传输敏感地理字段到日志或第三方服务


















