Nginx可通过GeoIP2模块识别用户地域并结合CORS配置实现按地区精准限制API访问:先加载GeoLite2数据库并提取国家码,再用map定义白名单,最后在location中拦截非授权地域请求,同时正确设置CORS响应头以支持合法跨域。

在 Nginx 中,通过 API 跨域访问控制 与 GeoIP 模块(或更推荐的 GeoIP2) 结合,可实现按用户地域精准限制 API 接口访问。核心思路是:先识别请求来源国家/地区,再根据预设白名单或黑名单决定是否放行;同时正确配置 CORS 头,确保合法跨域请求能被浏览器接受。
一、确认并启用 GeoIP2 模块(替代已废弃的 GeoIP)
Nginx 官方已不再维护旧版 GeoIP(基于.dat v1),推荐使用 ngx_http_geoip2_module + MaxMind GeoLite2 数据库。需提前编译安装或使用支持该模块的发行版(如 Ubuntu 的 nginx-extras)。
- 下载 GeoLite2 Country 数据库(免费版):https://dev.maxmind.com/geoip/geolite2-free-geolocation-data,解压后得到
GeoLite2-Country.mmdb - 在 nginx.conf 的
http块中加载数据库和变量:load_module modules/ngx_http_geoip2_module.so; # 编译安装时路径可能不同 geoip2 /path/to/GeoLite2-Country.mmdb { $geoip2_data_country_code source=$remote_addr country iso_code; } - 验证是否生效:添加一个测试 location,用
return 200 "$geoip2_data_country_code\n";,curl 测试返回对应国家码(如 CN、US)
二、为 API 接口配置跨域(CORS)头
仅限地域控制还不够——若前端页面域名与 API 域名不同,浏览器会因同源策略拦截响应。必须显式允许跨域,并注意与地域逻辑协同。
- 在 API 对应的
location块中设置标准 CORS 响应头:add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
- 对预检请求(OPTIONS)直接返回 204,不进入后端:
if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization'; add_header 'Access-Control-Max-Age' 86400; add_header 'Content-Type' 'text/plain; charset=utf-8'; add_header 'Content-Length' 0; return 204; } - ⚠️ 注意:
Access-Control-Allow-Origin: *与携带凭证(withCredentials: true)互斥。如需传 Cookie 或 Authorization,必须指定明确 Origin(可用变量动态设置)
三、结合地域变量实现精准限访
利用 $geoip2_data_country_code 变量,在 location 或 if 中做判断,拒绝非法地域请求。推荐用 map 预定义规则,更高效清晰。
- 在
http块中定义允许国家列表:map $geoip2_data_country_code $allowed_country { default 0; CN 1; JP 1; KR 1; SG 1; } - 在 API location 中拦截非授权地域:
location /api/ { if ($allowed_country = 0) { return 403 '{"error":"Access denied: your region is not supported"}'; } <pre class="brush:php;toolbar:false;"># 后续 proxy_pass 或其他处理... proxy_pass http://backend;}
- 进阶:按地域返回不同响应(如重定向到本地化页面)、记录访问日志(
log_format中加入$geoip2_data_country_code)
四、调试与注意事项
上线前务必验证行为是否符合预期,避免误杀或绕过。
- 用 curl 模拟不同地区 IP(配合代理或 Cloudflare Workers 修改 X-Forwarded-For)测试返回状态码和 CORS 头
- 确保真实客户端 IP 被正确传递:若前端有 CDN 或反向代理,需在 upstream 中设置
proxy_set_header X-Real-IP $remote_addr;,并在 geoip2 指令中用source=$http_x_real_ip - GeoIP2 数据库需定期更新(MaxMind 提供每月更新链接),可写脚本自动下载 + reload nginx
- 性能影响极小,但高频请求下 map 查表仍优于大量 if 嵌套

















