之前使用了ngx_http_geoip2_module
模块来对自己的博客进行限制国家访问,其实我的博客前面还套了一层cloudflae免费的CDN,还可以用cloudflare来实现这个功能,顺便还能规避使用免费的IP数据库数据更新不及时的问题。
要求:需要域名解析使用Cloudflare ,并且dns记录里开启代理
获取真实ip
在nginx限制国外ip访问结尾说使用cloudflare代理后获取不到访问者真实ip,这里先把这个解决一下
Cloudflare 使用 “CF-Connecting-IP” 标头和 “X-Forwarded-For” 标头发送源服务器访问者的 IP,要将原始访问者 IP 包含在日志中,我们在在 log_format 指令中添加变量 和http_x_forwarded_for即可
根据国家限制
根据国家做限制这里有两种实现方式
1、使用http标头在nginx里做
2、cloudflare上自定义安全规则
1、使用http标头
cloudflare提供了http标头CF-IPCountry
包含原始访客国家的两个字符的国家代码。
除了ISO-3166-1 alpha-2 代码之外,Cloudflare 还使用以下特殊国家代码:
XX
- 用于没有国家代码数据的客户,例如CN,US,JP
T1
- 用于使用 Tor 网络的客户端。
我们修改一下日志格式为,在日志中打印一下国家代码
log_format debuglog '
$remote_addr - $remote_user [$time_local] "$request"
Status: $status
Host: $host
UA: $http_user_agent
X-Real-IP: $http_x_real_ip
X-Forwarded-For: $http_x_forwarded_for
CF-Connecting-IP: $http_cf_connecting_ip
CF-IPCountry: $http_cf_ipcountry
Forwarded: $http_forwarded
All: $http_x_forwarded_for,$http_cf_connecting_ip,$http_x_real_ip
';
访问日志如下:
162.158.106.132 - - [14/Aug/2025:21:11:29 +0800]"GET /content.json?t=1755177088692 HTTP/1.1"
Status:200
Host: www.lishuai.fun
UA: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
X-Real-IP:162.158.106.132
X-Forwarded-For:162.158.106.132
CF-Connecting-IP:160.16.79.228
CF-IPCountry: JP
Forwarded: -
All:162.158.106.132,160.16.79.228,162.158.106.132
172.71.183.224 - - [14/Aug/2025:21:11:35 +0800]"GET /favicon.ico HTTP/1.1"
Status:404
Host: www.lishuai.fun
UA: Mozilla/5.067805899 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
X-Real-IP:172.71.183.224
X-Forwarded-For:172.71.183.224
CF-Connecting-IP:59.173.133.69
CF-IPCountry: CN
Forwarded: -
All:172.71.183.224,59.173.133.69,172.71.183.224
可以看到日志中打印出的CF-Connecting-IP
和CF-IPCountry
后的内容就是访客的源IP和访客的国家。
比如我们这里只允许访客IP为中国访问
在nginx配置文件的http段里添加
map $http_cf_ipcountry $cf_allowed_country {
default yes;
CN no;
}
在nginx配置文件server段添加
location @deny403 {
default_type 'text/html; charset=utf-8';
return 403 "<html><head><meta charset='UTF-8'></head><body><h1>Access Denied</h1>
<p>Your IP: $http_cf_connecting_ip</p>
<p>Your Country: $http_cf_ipcountry</p>
<p>请使用中国大陆IP访问</p>
<p>Access is only allowed from IP addresses located in mainland China.</p>
</body></html>";
}
# 使用cf返回的国家code限制
if ($cf_allowed_country = yes) {
return 403;
}
nginx -s reload
重载一下配置验证一下,可以看到我使用日本和美国的ip进行访问均被拒绝了
2、cloudflare上自定义安全规则
复原一下nginx配置,保证对访问无限制,在cloudflare控制台下找到
定义一个规则,针对www.lishuai.fun
阻止访客ip为Japan访问
注意: 这里规则会应用于cloudflare dns记录里所有开启代理的子域名上,如果需要对某个二级域名限制,可以使用主机名过滤一下,这里可以选择的很多,比如 ASN
,国家/地区
,请求方法
,标头
,http版本
,uri
,mime类型
等等,不过没有提供根据访问ip所属的城市来限制
我们这时候在使用日本ip访问就会发现,已经被拒绝了,并且403页面也是cloudflare提供的了
根据城市限制
启用添加访问者位置标头
的托管转换后,可以获取到更新位置相关的标头
将带有访问者 IP 地址位置信息的 HTTP 标头添加到发送到原始服务器的请求中:
cf-ipcity
:访客所在的城市(来自
ip.src.city
字段的值)。cf-ipcountry
:访客的国家(来自
ip.src.country
字段的值)。cf-ipcontinent
:访客的大陆(来自
ip.src.continent
字段的值)。cf-iplongitude
:访客的经度(来自
ip.src.lon
字段的值)。cf-iplatitude
:访客的纬度(来自
ip.src.lat
字段的值)。cf-region
:访客的地区(来自
ip.src.region
字段的值)。cf-region-code
:访客的地区代码(来自
ip.src.region_code
字段的值)。cf-metro-code
:访客的都市代码(来自
ip.src.metro_code
字段的值)。cf-postal-code
:访客的邮政编码(来自
ip.src.postal_code
字段的值)。cf-timezone
:访问者时区的名称(来自
ip.src.timezone.name
字段的值)。
注意:即使关闭**“添加访问者位置标头”**,也会向原始服务器发送HTTP 标头,cf-ipcountry
这也是上面不用修改任何设置在配置文件使用$http_cf_ipcountry
来对访客的国家做限制。
这时候我们就可以根据城市来限制访问了,修改nginx的配置文件,在http段添加如下日志文件格式,
log_format log_cf '$http_cf_connecting_ip $http_cf_ipcountry $http_cf_ipcity - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$scheme://$host$request_uri" $remote_addr '
'$http_cf_ipcontinent $http_cf_iplongitude $http_cf_iplatitude $http_cf_regio' ;
这个时候日志就根据我们的定义打印出了,国家和城市等信息
129.146.246.88 US Phoenix - - [14/Aug/2025:22:39:21 +0800] "GET /favicon.ico HTTP/1.1" 403 311 "https://www.lishuai.fun/2025/08/06/ollam-run-gpt-oss-20b/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "172.70.211.195" "http://www.lishuai.fun/favicon.ico" 172.70.211.195 NA -111.99840 33.46650 -
58.59.246.97 CN Nanning - - [14/Aug/2025:22:39:24 +0800] "GET /favicon.ico HTTP/1.1" 404 548 "-" "Mozilla/5.067805899 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36" "172.71.183.224" "http://www.lishuai.fun/favicon.ico" 172.71.183.224 AS 108.31667 22.81667 -
现在有两个ip,所属城市分别Osaka和Tokyo ,现在我想限制ip所属城市为Tokyo时禁止访问
修改nginx配置文件,在http段添加如下
map $http_cf_ipcity $cf_deny_city {
default no ;
Tokyo yes;
}
修改nginx配置文件,在server段下添加如下
location @cfdeny403 {
default_type 'text/html; charset=utf-8';
return 403 "<html><head><meta charset='UTF-8'></head><body><h1>Access Denied</h1>
<p>Your IP: $http_cf_connecting_ip</p>
<p>Your Country: $http_cf_ipcountry</p>
<p>Your City: $http_cf_ipcity</p>
<p>请使用中国大陆IP访问</p>
<p>Access is only allowed from IP addresses located in mainland China.</p>
</body></html>";
}
if ( $cf_deny_city = yes) {
return 403;
}
当使用ip所属城市为Tokyo访问
当使用ip所属城市为Osaka访问正常
通过日志我们也可以看出来,当访问者城市是Tokyo 状态码是403 ,当访问者城市是Osaka ,状态码是200
160.16.79.228 JP Tokyo - - [14/Aug/2025:23:10:03 +0800] "GET /ip.html HTTP/1.1" 403 346 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "172.69.165.80" "http://www.lishuai.fun/ip.html" 172.69.165.80 AS 139.69171 35.68950 -
219.94.232.108 JP Osaka - - [14/Aug/2025:23:10:12 +0800] "GET /ip.html HTTP/1.1" 403 205 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "162.158.108.110" "http://www.lishuai.fun/ip.html" 162.158.108.110 AS 135.50107 34.69379 -
219.94.232.108 JP Osaka - - [14/Aug/2025:23:10:21 +0800] "GET / HTTP/1.1" 200 18972 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "162.158.108.110" "http://www.lishuai.fun/" 162.158.108.110 AS 135.50107 34.69379 -
219.94.232.108 JP Osaka - - [14/Aug/2025:23:10:23 +0800] "GET /content.json?t=1755184223022 HTTP/1.1" 200 4867 "https://www.lishuai.fun/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" "162.158.108.110" "http://www.lishuai.fun/content.json?t=1755184223022" 162.158.108.110 AS 135.50107 34.69379 -
总结
个人最后还是使用cloudflare提供的标头的方式,通过获取到国家和城市,本来想着使用cloudflare提供的数据能更好的去使用goaccess去分析,并且也不用再去手动更新ip数据库里,并且cloudflare的ip数据库还是很准确的,相当于白嫖了商用IP数据库,结果发现goaccess 中根据ip显示的国家和城市只能通过指定ip数据库文件去解析,这个比较遗憾,不过还是可以根据cloudflare提供的国家和城市,以及goaccess的分析可以更精确的对一些访问进行限制。