Skip to content

网站按地区限制访问怎么做?IP白名单和区域封锁实战

很多业务都有"按地区控制访问"的需求:版权内容只允许国内观看、后台只允许公司 IP 登录、活动只对某些省市开放……这些需求背后都有同一个技术点——通过 IP 归属地判断用户位置。今天把几种常见的实现方案和坑都讲清楚。

一、典型的地区控制场景

  • 内容版权限制:视频、音乐、电子书按国家/地区授权,非授权地区直接返回"所在地区不可观看";
  • 后台安全:管理后台只允许公司办公网段的 IP 访问,其他 IP 一律拦截;
  • 活动限定:电商大促只对指定省市开放,防黄牛跨区抢购;
  • 合规要求:某些行业(如外汇、彩票类)需要限制特定地区用户访问;
  • 防攻击:海外攻击源多,直接封禁境外 IP 能挡掉一大半扫描和暴力破解。

二、实现方案

方案一:Nginx 层做地区判断(推荐)

在 Nginx 里用 IP 归属地模块,请求进来先查归属地,命中黑名单地区直接返回 403。优点是在入口就挡掉,不消耗后端资源。

nginx
# 需要安装 ngx_http_geoip2_module 并准备 IP 归属地数据库
geoip2 /etc/nginx/geoip/GeoLite2-City.mmdb {
    $geoip2_data_country_code country iso_code;
    $geoip2_data_city_name city names zh-CN;
}

# 禁止非中国地区访问 /admin 后台
location /admin {
    if ($geoip2_data_country_code != "CN") {
        return 403;
    }
    proxy_pass http://backend;
}

方案二:应用层调用 IP 查询 API(灵活,适合动态规则)

如果地区规则经常变(比如活动期间临时开放某省),在应用层做更灵活。以 Python 为例:

python
import requests
from flask import request, abort

def get_ip_location(ip):
    """调用 IP9 免费接口查询归属地"""
    resp = requests.get(f"https://ip9.com.cn/get?ip={ip}", timeout=3)
    data = resp.json()
    if data.get("ret") == 200:
        return data["data"]
    return None

ALLOWED_PROVS = {"北京", "上海", "广东"}  # 活动开放省份

@app.before_request
def check_region():
    ip = request.headers.get("X-Forwarded-For", request.remote_addr).split(",")[0]
    info = get_ip_location(ip.strip())
    if info and info.get("country") == "中国" and info.get("prov") in ALLOWED_PROVS:
        return  # 放行
    abort(403, description="当前地区暂未开放")

方案三:后台 IP 白名单

后台安全最简单的做法:只放行白名单 IP。

python
ADMIN_WHITELIST = {"203.0.113.10", "203.0.113.11"}  # 公司出口 IP

@app.before_request
def check_admin_whitelist():
    if request.path.startswith("/admin"):
        ip = request.headers.get("X-Forwarded-For", request.remote_addr).split(",")[0]
        if ip.strip() not in ADMIN_WHITELIST:
            abort(403)

注意:如果公司出口是动态 IP(家庭宽带拨号),白名单会误伤。更稳的做法是"白名单网段 + 登录二次验证"双保险。

三、必须避开的坑

1. 拿不到真实 IP

网站前面挂了 CDN 或负载均衡后,remote_addr 是 CDN 节点 IP,不是用户 IP。必须:

python
# 优先取 X-Forwarded-For,但要防伪造
ip = request.headers.get("X-Forwarded-For", request.remote_addr)
real_ip = ip.split(",")[0].strip()

防伪造X-Forwarded-For 用户可以自己伪造,只信 CDN 回源加的自定义头(如 X-Real-IP,且要在 CDN 配置里禁止用户传入)。

2. 移动网络误伤

手机基站分配的 IP 归属地可能在省内飘,用 4G/5G 的合法用户偶尔会被误判。别做"一刀切封禁",给误判留申诉通道,或者对移动网络(ISP 类型)放宽规则。

3. IPv6 用户

现在很多家庭宽带和手机已经是 IPv6,只查 IPv4 会漏掉。查询接口要 IPv4/IPv6 都支持——IP9 的接口 https://ip9.com.cn/get?ip=2400:3200::1 可以直接查 IPv6。

4. 代理绕过

用 VPN/代理可以轻松改"归属地",所以地区限制防的是"普通人",不是"高手"。对安全要求高的场景,地区判断只是第一道门槛,还要配合设备指纹、行为分析。

四、性能考虑

每个请求都实时查接口会有延迟和限流压力,生产环境建议:

  1. 本地缓存:IP 归属地短期不变,缓存 10-60 分钟;
  2. 只对敏感路径做检查:没必要全站每个请求都查,只查 /admin、下单接口等关键路径;
  3. 升级付费版:免费版 60 次/分钟,高并发场景用 VIP 版(18 万次/分钟),或直接用 GeoIP 离线库。

总结

按地区限制访问的完整链路是:拿到真实 IP → 查询归属地 → 匹配规则 → 放行或拦截。小流量用免费 API 完全够,大流量建议 Nginx 离线库或付费接口。不管哪种方案,记住三个关键词:真实 IP、缓存、别一刀切

IP9 官网:https://www.ip9.com.cn