建站系列之为 Nginx 安装 GeoIP 模块实现分流或屏蔽某个地区访问

小助手读文章 00:00 / 00:00

有时候我们想屏蔽某个地区的 IP 访问,或者根据访问来源转向不同的子站实现分流,此时如果用防火墙规则把 IP 重定向到预定页面并不是特别灵活的办法,特别是一个 IP 上拥有运行多个站点的情况下,正统的办法应该是用 GeoIP 配合对应的 web 服务器模块来实现需求,比如:Apache + Mod_GeoIp 或者 Nginx + Http_GeoIp_Module 等。

以下以 Nginx + GeoIP 为例,已预装 Nginx,基于宝塔。

一、安装运行库

GeoIP 基于 MaxMind GeoIp 产品,需要先安装相应运行库:

# yum install epel-release -y
# yum install geoip-devel -y

Ubuntu:apt-get install libgeoip-dev

二、重新编译

Nginx 已经预装,通过 nginx -V 查看之前的编译参数:

# nginx version: nginx/1.10.3
......
configure arguments: --user=www --group=www --prefix=/www/server/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_gzip_static_module --with-stream --with-stream_ssl_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --add-module=/www/server/nginx/src/src/incubator-pagespeed-ngx-1.13.35.2-stable --add-module=/www/server/nginx/src/src/ngx_brotli

要启用 GeoIP 模块只需要在编译参数后面加上 --with-http_geoip_module 即可

# cd /www/server/nginx/src/
# ./configure --user=www --group=www --prefix=/www/server/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_gzip_static_module --with-stream --with-stream_ssl_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --add-module=/www/server/nginx/src/src/incubator-pagespeed-ngx-1.13.35.2-stable --add-module=/www/server/nginx/src/src/ngx_brotli --with-http_geoip_module
# service nginx stop
# make && make install
# service nginx start

要验证是否成功启用模块,可以使用如下命令:

# ldd /www/server/nginx/sbin/nginx
......
    libGeoIP.so.1 => /lib64/libGeoIP.so.1 (0x00007f9f080d9000)
......

有显示如上字样说明安装成功。

三、修改配置

模块安装成功后,还要在 Nginx 里指定数据库,在安装运行库时默认安装了两个,位于 /usr/share/GeoIP/ 目录下,一个只有 IPv4,一个包含 IPv4 和 IPv6:

# ls /usr/share/GeoIP/
GeoIP.dat  GeoIP-initial.dat  GeoIPv6.dat  GeoIPv6-initial.dat

找到数据库文件后,就要修改 nginx 配置,在 http 段增加:

# vim /www/server/nginx/conf/nginx.conf
......
# 配置一个即可;
      geoip_country /usr/share/GeoIP/GeoIP.dat;
#     geoip_city /usr/share/GeoIP/GeoLiteCityv6.dat;
......
# nginx -t
nginx: the configuration file /www/server/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /www/server/nginx/conf/nginx.conf test is successful
# service nginx reload
Reload service nginx...  done

由于 IP 广播泛滥,所以 GeoIP 并不是那么准确,如果觉得 GeoIP 库太旧了,可以自行到官网下载最新版,将上述配置的路径改一下即可。

四、使用方法

GeoIP 参数

使用 GeoIP 数据库

$geoip_country_code; - 国家名的前两个字母, 如, "RU", "US";
$geoip_country_code3; - 国家名的前三个字母, 如, "RUS", "USA";
$geoip_country_name; - 国家名称, 如, "Russian Federation", "United States";

使用 GeoLiteCity 数据库

$geoip_city_country_code; -国家名的前两个字母, 如, "RU", "US";
$geoip_city_country_code3; - 国家名的前三个字母, 如, "RUS", "USA";
$geoip_city_country_name; -国家名称, 如, "Russian Federation", "United States";
$geoip_region; - 省,州或区名 (province, region, state, province, federal land, and the like), 如, "Moscow City", "DC";
$geoip_city; - 城市名称, 如, "Moscow", "Washington";
$geoip_postal_code; - 邮政编号;

使用示例

屏蔽国家

使用 GeoIP 数据库

# vim /www/server/nginx/conf/vhost/test.conf
......
location / {
default_type text/html;
charset utf-8;
if ( $geoip_country_code = CN ) {
  return 502 "无权访问!";
}
}
......

注:注意空格,少了会报错。

屏蔽城市

使用 GeoLiteCity 数据库

# vim /www/server/nginx/conf/vhost/test.conf
......
location / {
default_type text/html;
charset utf-8;
if ( $geoip_city = Beijing ) {
  return 502 "无权访问!";
}
}
......

注:城市列表可参考官方 CSV 文档

屏蔽省份

使用 GeoLiteCity 数据库

# vim /www/server/nginx/conf/vhost/test.conf
......
location / {
default_type text/html;
charset utf-8;
if ( $geoip_region = 22 ) {
  return 502 "无权访问!";
}
}
......

注:实际测试发现屏蔽省份不能使用说明中的 Beijing、Guangdong 等字眼,需要使用 ISO 3166 规定的代码,代码参见:官方 CSV 文档
但与此同时又产生一个让人不解的问题,二位数字代码不是唯一的,比如本例中的 22,既可表示 Beijing,又可表示 Goycay,GeoIP 如何保证对应呢?


参考文章:
1、《HttpGeoIP - 中文文档
2、《Nginx增加GeoIP模块实现屏蔽某个国家的IP访问
3、《Debian/Ubuntu下安装GeoIP


ArmxMod for Typecho
个性化、自适应、功能强大的响应式主题

推广

 继续浏览关于 nginx教程屏蔽建站geoip分流 的文章

 本文最后更新于 2018/07/24 07:12:00,可能因经年累月而与现状有所差异

 引用转载请注明:VirCloud's Blog > 建站 > 建站系列之为 Nginx 安装 GeoIP 模块实现分流或屏蔽某个地区访问

精选评论

  1. 猫

    博主,有个新问题想请教一下,目前网站已经对IP做了限制,所有来自国内的IP访问网站的所有网页时都返回403。
    但是现在有这样一个需求,就是在网站中,有一个网页,比如是http://localhost/123.php,我希望这个地址能让所有的IP都能访问到,即便是来自国内的IP也能正常访问,应该如何写呢?

    1. 欧文斯

      网站配置里增加 location /123.php { allow all;} 估计可以,位置的话由于 Nginx 配置先后执行顺序有点复杂,放在屏蔽配置的前后都试试。

      1. 猫

        尝试过这个方法了,但是似乎没效果,我再研究下。

        1. yuui

          请问是否实现了这个功能呢?可以分享下经验吗

  2. 使用 CDN(CloudFlare|腾讯云|加速乐等)情况下如何获取访客真实 IP ? R11; 羊毛党
  3. 猫

    博主 想问一下,使用了geoip模块后,用VPN也无法访问 是正常的吗?我现在是设置屏蔽中国大陆访问,但是我使用VPN之后也访问不了。

    1. 欧文斯

      VPN 是否是大陆 IP ?不是的话那就是配置有问题了,仔细检查下代码

      1. 猫

        找到问题所在了,因为我使用了cloudflare的CDN,似乎是因为Cloudflare会将所有访客显示为是由cloudflare的IP地址而来的。

        1. 欧文斯

          你要配置下显示真实 IP,参考:http://t.cn/Eq5rFe2

          1. 欧文斯

            显然可以的,规则匹配下就好了

          2. 海德思

            感谢分享!解决了

            顺便想问下博主,能不能设置返回一个指定的html页面?

          3. 欧文斯
          4. 猫

            感谢,已经解决,不过用的另一种方法
            Nginx添加
            map $http_cf_ipcountry $allow {
            default yes;
            CN no;
            }
            然后在站点配置文件添加
            if ($allow = no) {
            return 403;
            }

  4. 猫

    博主你好,遇到个新问题,现在的版本已经是geoip2版本,数据库格式是mmdb的格式了,以前的dat格式的没了,现在我已经添加了geoip2模块到nginx中,但是应该如何应用到网站上呢?我在网上看了很多都是说要在nginx的配置文件中load_module一个ngx_http_geoip2_module.so,但是我一直没有找到这个ngx_http_geoip2_module.so在什么地方。~

    1. 欧文斯

      按文中方法就可以了

  5. 猫

    博主,想问一下,我也用的宝塔,一开始直接一键安装的nginx,不是编译暗转的,现在想加入geoip模块,应该怎么操作呢?

    1. 欧文斯

      不管是一键安装,还是编译安装,新增模块的方法都是一样的,就如文中所说,在原来的编译参数基础上(nginx -V) 查看,加上要新增的模块参数,然后 make 后替换 nginx 可执行文件就可以了。

  6. 欧文斯