Nginx使用Geoip2模块 解析客户端IP地理信息 限定国家区域访问

2025-07-27 74 0

使用Nginx Geoip2模块 解析客户端IP地理信息 可以对指定国家区域进行限制访问

ngx_http_geoip2_module

libmaxminddb

ngx_http_geoip2_module 依赖libmaxminddb

https://github.com/maxmind/libmaxminddb

Ubuntu 有 apt源 可以直接安装,也可以编译安装

sudo add-apt-repository ppa:maxmind/ppa
sudo apt update
sudo apt install libmaxminddb0 libmaxminddb-dev mmdb-bin

ngx_http_geoip2_module

编译时不能同时 with-http_geoip_module 使用过程会冲突
https://github.com/leev/ngx_http_geoip2_module/issues/92

git clone https://github.com/leev/ngx_http_geoip2_module.git
./configure \
    --prefix=/usr/local/openresty \
    --user=www \
    --group=www \
    --with-file-aio \
    --with-poll_module \
    --with-http_realip_module \
    --with-http_image_filter_module \
    --with-http_gunzip_module \
    --with-http_gzip_static_module \
    --with-http_addition_module \
    --with-http_sub_module \
    --with-http_dav_module \
    --with-http_flv_module \
    --with-http_mp4_module \
    --with-http_slice_module \
    --with-http_random_index_module \
    --with-http_secure_link_module \
    --with-http_degradation_module \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_stub_status_module \
    --with-http_slice_module \
    --with-http_auth_request_module \
    --with-stream \
    --with-stream_ssl_module \
    --with-stream_ssl_preread_module \
    --with-stream_realip_module \
    --with-threads \
    --with-pcre \
    --with-compat \
    --with-stream \
    --with-stream_ssl_module \
    --add-module=/root/ngx_http_geoip2_module

    # with-http_geoip_module 不能同时使用

编译成功后,手动复制nginx二进制执行文件

make -j 8

生产业务不能停机,这里采用平滑升级

mv /usr/local/openresty/nginx/sbin/nginx /usr/local/webserver/nginx/sbin/nginx_old
cp ./build/nginx-1.25.3/objs/nginx /usr/local/openresty/nginx/sbin/

# 测试
nginx -t
# 查看模块
nginx -V

若刷新配置文件nginx -s reload不生效,这里再执行多一次,测试环境则可以简单粗暴systemctl restart nginx

kill -USR2 `cat /usr/local/openresty/nginx/logs/nginx.pid`
sleep 1
kill -QUIT `cat /usr/local/openresty/nginx/logs/nginx.pid.oldbin`

GeoIP2地址库

maxmind官方注册有点麻烦,这里使用
https://github.com/P3TERX/GeoLite.mmdb

mkdir /opt/geoip
cd /opt/geoip
wget https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-City.mmdb
wget https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-Country.mmdb
$ mmdblookup --file GeoLite2-City.mmdb --ip 119.29.29.29

  {
    "city": 
      {
        "geoname_id": 
          1809858 <uint32>
        "names": 
          {
            "de": 
              "Guangzhou" <utf8_string>
            "en": 
              "Guangzhou" <utf8_string>
            "es": 
              "Cantón" <utf8_string>
            "fr": 
              "Canton" <utf8_string>
            "ja": 
              "広州" <utf8_string>
            "pt-BR": 
              "Cantão" <utf8_string>
            "ru": 
              "Гуанчжоу" <utf8_string>
            "zh-CN": 
              "广州市" <utf8_string>
          }
      }
...

mmdblookup 会返回 JSON 格式的数据,字段路径就是 JSON 的路径,数组字段直接用下标索引即可。

http {
  geoip2 <db_path> {
    <$variable_name> default=<default_value> source=<$variable> <path>;
    // ...
  }
}
  • db_path 为GeoIP2 地址库路径,建议使用绝对路径,相对路径为Nginx 安装目录;
  • variable_name 为定义变量名,用于存储获取到的地理位置信息,比如 geoip2_city_name和geoip2_country_name 等;
  • default_value 为当获取不到地理位置信息时的默认值;
  • source 为要解析的IP 地址,默认为$remote_addr 变量,若 Nginx 前面还有一层负载网关,可以按实际调整IP变量, 如$http_x_forwarded_for$http_x_real_ip

解析国家使用 GeoLite2-Country.mmdb
解析国家城市等使用 GeoLite2-City.mmdb

http {
    map $http_x_forwarded_for $real_ip {
        default $remote_addr;
        "~^(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$" $ip;
        "~^(?P<first_ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}),.*$" $first_ip;
    }

#    geoip2 /opt/geoip/GeoLite2-Country.mmdb {
#        auto_reload 5m; 
#        $geoip2_metadata_country_build metadata build_epoch;
#        $geoip_country_code default=Unknown source=$real_ip country iso_code;
#        $geoip_country_name default=Unknown source=$real_ip country names en; 
#    }

    geoip2 /opt/geoip/GeoLite2-City.mmdb {
       $geoip_country_code default=Unknown source=$real_ip country iso_code;
       $geoip_province_name default=Unknown source=$real_ip subdivisions 0 names en; 
       $geoip_city_name default=Unknown source=$real_ip city names en;
    }

    log_format sundayhk_json '{'
        '"@timestamp": "$time_iso8601",'
        '"remote_addr": "$remote_addr",'
        '"real_ip": "$real_ip,'
        '"host": "$host",'
        '"request_method": "$request_method",'
        '"request_uri": "$request_uri", '
        '"uri": "$uri", '
        '"status": $status,'
        '"request_time": $request_time,'
        '"request_length": $request_length,'
        #'"request_body":"$request_body",'
        '"body_bytes_sent": $body_bytes_sent,'
        '"bytes_sent": $bytes_sent,'
        '"http_referer": "$http_referer",'
        '"http_user_agent": "$http_user_agent",'
        #'"http_x_request_id":"$http_x_request_id",'
        '"http_x_forwarded_for": "$http_x_forwarded_for",'
        '"upstream_addr": "$upstream_addr",'
        '"upstream_status": "$upstream_status",'
        '"upstream_response_time": "$upstream_response_time",'
        '"upstream_cache_status": "$upstream_cache_status",'
        '"server_addr":"$server_addr",'
        '"server_port":$server_port,'
        '"server_protocol":"$server_protocol",'
        '"remote_user":"$remote_user",'
        '"ssl_protocol":"$ssl_protocol",'
        '"ssl_cipher":"$ssl_cipher"'
        '"geoip_country_code": "$geoip_country_code",'
        '"geoip_province_name": "$geoip_province_name",'
        '"geoip_city_name": "$geoip_city_name"'
    '}';
    access_log  /data/logs/nginx/access_json.log sundayhk_json;
}
$ tail /data/logs/nginx/access_json.log
{"@timestamp": "2024-07-28T11:07:42+08:00","remote_addr": "116.76.39.99","real_ip": "116.76.39.99,"host": "www.sundayhk.com","request_method": "GET","request_uri": "/albums/page/101", "uri": "/albums/page/101", "status": 301,"request_time": 0.000,"request_length": 413,"body_bytes_sent": 182,"bytes_sent": 383,"http_referer": "-","http_user_agent": "Mozilla/5.0 (Linux; U; Android 7.1.1; zh-cn; OPPO R9sk Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.4511.139 Mobile Safari/537.36 OppoBrowser/10.5.1.2","http_x_forwarded_for": "-","upstream_addr": "-","upstream_status": "-","upstream_response_time": "-","upstream_cache_status": "-","server_addr":"64.181.239.33","server_port":80,"server_protocol":"HTTP/1.1","remote_user":"-","ssl_protocol":"-","ssl_cipher":"-""geoip_country_code": "CN","geoip_province_name": "Guangdong","geoip_city_name": "Shenzhen"}

传递IP地理变量给上游服务

http {
  server {
    listen 80;

    location / {
      proxy_pass http://upstream;
      proxy_set_header X-Real-IP $real_ip;
      proxy_set_header X-Country-Code $geoip_country_code;
      proxy_set_header X-Province-Name $geoip_province_name;
      proxy_set_header X-City-Name $geoip_city_name;
    }

    location /php {
      fastcgi_pass php:9000;
      fastcgi_param REMOTE_ADDR $real_ip;
      fastcgi_param GEOIP_COUNTRY_Code $geoip_country_code;
      fastcgi_param GEOIP_PROVINCE_NAME $geoip_province_name;
      fastcgi_param GEOIP_CITY_NAME $geoip_city_name;
    }
  }
}

限制指定国家访问

http {
  map $geoip_country_code $is_country_code_allowed {
    CN 1;
    default 0;
  }

  server {
    listen 80;

    location / {
      if ($is_country_code_allowed = 0) {
        return 403 "Forbidden";
      }

      return 200 "Welcome!";
    }
  }
}

不同国家IP显示不同语言

http {
      map $geoip_country_code $lang_path {
        default      /;      # 默认路径(英文)
        CN           /cn/;    # 中国用户访问 /cn/
        JP           /jp/;    # 日本用户访问 /jp/
        US           /en/;
        GB           /en/;
        FR           /fr/;
    }
    server {
        listen 80;
        server_name your_domain.com; # 替换为你的域名

        # 重定向逻辑
        location / {
            # 检查当前请求的 URI 是否已经包含了语言路径
            # 避免重复重定向,例如用户手动访问 your_domain.com/cn/ 时不再重定向
            if ($request_uri !~ ^/(cn|jp|en|fr)/) {
                # 显式URL改变(例如从 example.com 到 example.com/cn/)
                rewrite ^ $scheme://$host$lang_path$request_uri? permanent;
                # 保持原URL不变,避免302/301 重定向,
                # rewrite ^/(.*)$ $lang_path$1 break;
            }
        }

        # 可以为不同的语言路径配置不同的根目录或处理逻辑
        # location /cn/ {
        #     root /path/to/your/website/root/cn;
        #     index index.html;
        #     try_files $uri $uri/ =404;
        # }
        #
        # location /jp/ {
        #     root /path/to/your/website/root/jp;
        #     index index.html;
        #     try_files $uri $uri/ =404;
        # }
    }
}

相关文章

Nginx后端获取CDN用户真实IP
Nginx 允许多个域名跨域访问
nginx-module-vts监控虚拟主机的流量
Nginx上传大文件慢 解决
nginx 1.22 编译webdav插件
ubuntu 22.04 编译 nginx 1.22

发布评论