你是否还在为如何有效限制特定地区用户访问服务器资源而烦恼?是否需要一个轻量级解决方案来管理不同区域的访问权限?本文将详细介绍如何使用Nginx的geo模块实现高效的区域访问控制,帮助你轻松解决这些问题。
读完本文后,你将能够:
Nginx geo模块(ngx_stream_geo_module)是一个用于根据客户端IP地址创建变量的模块。它允许将IP地址映射到预定义的值,从而实现基于地理位置的访问控制、内容定制等功能。
geo模块通过创建一个IP地址到值的映射表来工作。当请求到达时,模块会根据客户端IP地址查找映射表,并为匹配的条目设置相应的变量值。这个变量随后可以在Nginx配置中用于条件判断,实现访问控制。

geo模块的核心实现位于src/stream/ngx_stream_geo_module.c文件中,包含以下关键组件:
static ngx_stream_module_t ngx_stream_geo_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL /* merge server configuration */
};
ngx_module_t ngx_stream_geo_module = {
NGX_MODULE_V1,
&ngx_stream_geo_module_ctx, /* module context */
ngx_stream_geo_commands, /* module directives */
NGX_STREAM_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
geo模块的基本配置语法如下:
geo $variable {
default value;
192.168.1.0/24 value1;
10.0.0.0/8 value2;
...
}
以下是一个基本的IP地址到区域的映射配置:
http {
# 定义IP到区域的映射
geo $region {
default unknown;
192.168.1.0/24 lan;
10.0.0.0/8 internal;
202.102.133.0/24 beijing;
113.108.216.0/24 guangzhou;
}
server {
listen 80;
server_name example.com;
# 根据区域变量进行访问控制
location / {
if ($region = unknown) {
return 403;
}
root html;
index index.html;
}
}
}
geo模块创建的变量可以在Nginx配置的其他部分使用,例如:
# 记录区域信息到日志
log_format main '$remote_addr [$time_local] "$request" '
'$status $region "$http_user_agent"';
access_log logs/access.log main;
# 根据区域定制内容
location / {
if ($region = beijing) {
rewrite ^ /beijing$request_uri last;
}
if ($region = guangzhou) {
rewrite ^ /guangzhou$request_uri last;
}
root html;
index index.html;
}
CIDR(无类别域间路由)表示法允许你指定IP地址范围:
geo $region {
default unknown;
192.168.1.0/24 lan; # 24位掩码,256个地址
10.0.0.0/8 internal; # 8位掩码,1600万个地址
2001:0db8::/32 ipv6; # IPv6地址范围
}
使用ranges指令可以定义连续的IP地址范围:
geo $region {
ranges;
default unknown;
192.168.1.1-192.168.1.100 lan;
192.168.1.101-192.168.1.200 dmz;
192.168.1.201-192.168.1.254 special;
}
使用delete指令可以从之前定义的范围中排除特定IP:
geo $region {
ranges;
default unknown;
10.0.0.0-10.255.255.255 internal;
delete 10.1.1.0-10.1.1.255; # 排除这个范围
10.1.1.100-10.1.1.150 dmz; # 重新定义排除范围内的部分地址
}
以下是一个基于国家/地区的访问控制示例:
geo $allowed_country {
default 0;
# 特定区域IP段示例
1.0.1.0/24 1;
1.0.2.0/23 1;
1.0.8.0/21 1;
# ... 其他区域IP段 ...
# 特定区域IP段示例
1.1.1.0/24 1;
2.2.2.0/24 1;
# ... 其他区域IP段 ...
}
server {
listen 80;
server_name example.com;
location / {
if ($allowed_country = 0) {
return 403 "Access denied: This content is not available in your region.";
}
root html;
index index.html;
}
}
结合limit_req模块实现基于区域的限速:
geo $limit_rate {
default 100k; # 默认限制100KB/s
192.168.1.0/24 1000k; # 局域网1MB/s
10.0.0.0/8 500k; # 内部网络500KB/s
}
limit_req_zone $binary_remote_addr zone=zone1:10m rate=10r/s;
server {
listen 80;
location /downloads {
limit_req zone=zone1 burst=20 nodelay;
limit_rate $limit_rate;
alias /var/www/downloads;
}
}
结合allow和deny指令实现IP过滤:
geo $ip_whitelist {
default 0;
192.168.1.0/24 1;
10.0.0.0/8 1;
127.0.0.1 1;
}
geo $ip_blacklist {
default 0;
203.0.113.0/24 1;
198.51.100.0/24 1;
}
server {
listen 80;
location /admin {
# 先检查黑名单
if ($ip_blacklist = 1) {
return 403;
}
# 再检查白名单
if ($ip_whitelist = 0) {
return 403;
}
# 白名单内的IP允许访问
root html/admin;
index index.html;
}
}
对于大量IP映射,使用二进制地址库可以提高性能:
geo $region {
ranges;
include /etc/nginx/geo/regions.bin; # 二进制格式的地址库
}
Nginx可以通过ngx_stream_geo_create_binary_base函数创建二进制地址库:
static void ngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx) {
// 创建二进制地址库的实现
}
geo模块使用高效的地址查找算法,包括:
// IPv4地址查找
vv = (ngx_stream_variable_value_t *)
ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
// IPv6地址查找
vv = (ngx_stream_variable_value_t *)
ngx_radix128tree_find(ctx->u.trees.tree6, p);
问题:geo配置似乎没有生效,所有请求都被归类为default。
解决方案:
# 验证Nginx配置
nginx -t
# 查看Nginx编译模块
nginx -V
问题:geo模块不识别IPv6地址。
解决方案:
geo $ipv6_region {
default unknown;
2001:0db8::/32 ipv6_test;
2001:4860:4860::8888/128 google_dns;
}
问题:IP地址库过大,导致Nginx启动缓慢或内存占用过高。
解决方案:
# 使用include拆分大型配置
geo $region {
include /etc/nginx/geo/base.conf;
include /etc/nginx/geo/cn.conf;
include /etc/nginx/geo/us.conf;
}
Nginx提供了另一个相关模块ngx_http_geoip_module,可以结合MaxMind的GeoIP数据库使用:
http {
# 加载GeoIP数据库
geoip_country /etc/nginx/geoip/GeoIP.dat;
geoip_city /etc/nginx/geoip/GeoLiteCity.dat;
# 使用GeoIP变量
location / {
if ($geoip_country_code !~ ^(CN|US)$) {
return 403;
}
add_header X-Country $geoip_country_name;
add_header X-City $geoip_city;
root html;
index index.html;
}
}
结合ngx_http_map_module实现更复杂的条件逻辑:
geo $region {
default unknown;
192.168.1.0/24 lan;
10.0.0.0/8 internal;
202.102.133.0/24 beijing;
}
map $region $access_level {
unknown 0;
lan 10;
internal 5;
beijing 3;
default 0;
}
server {
location /api {
if ($access_level < 3) {
return 403;
}
proxy_pass http://api_server;
}
location /admin {
if ($access_level < 10) {
return 403;
}
proxy_pass http://admin_server;
}
}
随着互联网的发展,地理位置访问控制将面临新的挑战和机遇:
通过合理配置和优化,Nginx的geo模块可以成为保护服务器资源、优化用户体验的强大工具。无论是小型网站还是大型企业应用,都可以从中受益。

