惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
小众软件
小众软件
博客园 - 叶小钗
宝玉的分享
宝玉的分享
博客园_首页
Hugging Face - Blog
Hugging Face - Blog
人人都是产品经理
人人都是产品经理
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
SegmentFault 最新的问题
B
Blog RSS Feed
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
Google DeepMind News
Google DeepMind News
U
Unit 42
F
Fortinet All Blogs
IT之家
IT之家
Y
Y Combinator Blog
Martin Fowler
Martin Fowler
T
The Blog of Author Tim Ferriss
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The GitHub Blog
The GitHub Blog
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
酷 壳 – CoolShell
酷 壳 – CoolShell

涛叔

友链可能导致网站掉备案 使用 DDNS 动态更新 ZZ.AC 域名 另一种轻量的 Linux VPS 共享方案 DMIT 主机自定义 IPv6 地址 自用 Claude Code 拼车中转服务 欢迎使用 @ZZ.AC 邮箱服务 我在用的 VPS 云主机 实现备份3-2-1原则 Go语言接口的空指针问题 玩转 ZZ.AC 域名 ZZ.NIC 公益 Linux 主机空间 使用 Nginx 构建计费 DoH 代理服务 通过 Let’s Encrypt 为 IP 地址签发免费 SSL 证书
基于 Nginx + GeoLite2 搭建简易 IP 查询工具
2026-09-04 · via 涛叔

家宽通常只有动态公网 IP 地址。为了能访问家庭内部网络,我们需要通过 DDNS1 技术将变化的公网 IP 地址自动映射到某个固定的域名上。如果 DDNS 工具运行在边缘路由器上,它可以直接获得公网 IP 地址;如果 DDNS 工具是运行在局域网内的一台主机上,获取当前的出口公网 IP 就会麻烦一些。最简单的办法就是通过 curl 访问类似 ip.network 这类可以返回调用方公网 IP 地址的服务。我一直用 ip.network 确认自己的公网 IP。但是今天收到告警邮件,发现我被 ip.network 给封锁了,可能是嫌我每五分钟调一次太多了😂所以我赶紧基于 Nginx 和 MaxMind 提供的免费 IP 地址库搭了一套简易的 IP 查询工具。今天把方案分享给大家。

大家可以直接访问 ip.zz.ac 查看自己的公网 IP 地址。如果想进一步查看 IP 所属的国家和 ASN,则需要访问 ip.zz.ac/more。最完整的 JSON 信息可以通过 ip.zz.ac/json 来查看。如果要查询其他 IP 地址,可以直接通过查询参数指定,比如curl -L ip.zz.ac?ip=1.1.1.1

✏️Note

如果用 curl 访问,请添加-L参数来自动处理 301 跳转 curl -L ip.zz.ac 或者指定走 HTTP 协议 curl https://ip.zz.ac

先实现返回当前客户端的公网 IP 地址。该功能非常简单,只需要 Nginx 就能做到。

简单来说就是配置一个特殊的 web 站点,通过return指令和$remote_addr变量来返回客户端的 IP 地址:

server {
    listen 443;
    server_name ip.zz.ac;
    ssl_certificate /etc/nginx/ssl/ip.zz.ac/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/ip.zz.ac/key.pem;

    location = / {
        return 200 '$remote_addr\n';
    }
}

如果你的 Nginx 直接监听公网 IP 地址,有上面这段站点配置就足够了。加载配置后访问 ip.zz.ac就会返回自己的公网 IP 地址。

💡Tip

主流操作系统会优先使用 IPv6 通信。如果你的设备支持 IPv6 网络,上面的配置就会返回 IPv6 公网地址。如果返回 IPv4 地址直,需要给 curl 添加 -4参数来强制走 IPv4 通信。为明确起见,我们最好给 curl 指定 -6 或者-4 来显示要求走哪种网络。

如果你的 Nginx 没有直接监听公网 IP 地址,例如是通过另外一个 LoadBalancer 来转发请求的,那么$remote_addr保存的就是 LB 到你 Nginx 通信所用的 IP 地址,而并非客户端的公网 IP 地址。此时你需要通过set_real_ip_fromreal_ip_header来更正当前 Nginx 的$remote_addr变量。具体操作请自行AI。

到这一步对 DDNS 来说已经足够了。但如果对外提供服务,还是太单薄了些。最好是在功能上尽量贴近 ip.network。所以我希望 ip.zz.ac 能提供较为丰富的 IP 信息。但优质的信息需要付费。公益服务显然无法承担商业版 IP 地址信息库,于是我想到了 MaxMind 提供的免费数据库 GeoLite2。

GeoLite2 数据库免费提供 IP 地址到国家、城市和运营商的映射信息。与付费版相比,它的数据精度略差,有些数据的字段不完整,有些字段不提供。但总体来说还是比较准确的,尤其是到国家一级,基本没有太大问题。MaxMind 不仅免费提供,而且定期更新,还要什么自行车🚴

但免费归免费,要想获得最新的 GeoLite2 数据,你还需要到 MaxMind 注册账号2,之后才能通过 LicenseKey 来下载数据。

在 ubuntu 下我样可以通过 geoipupdate 来自动更新 GeoLite2 数据。它的配置文件路径为/etc/GeoIP.conf,内容就三行:

AccountID 1000
LicenseKey XXXX
EditionIDs GeoLite2-Country GeoLite2-City GeoLite2-ASN

前两项登录 MaxMind 后台获取,最后一项指定需要同步的数据库。我这里同时下载了国家、城市和 ASN 三个数据库。全都要!配置好之后开启定时更新服务:

systemctl enable --now geoipupdate.service

更新的数据目录为/var/lib/GeoIP/,里面有三个对应的mmdb文件。

接下来我们将 GeoLite2 集成到 Nginx 中。

Nginx 官方提供 ngx_http_geoip_module 模块。然而,该模块只能加载老式的 GeoIP 数据,并不支持 GeoLite2🤦‍♂️Nginx 官方虽然提供 GeoIP23 但仅限 Plus 用户。于是乎便有了开源的 ngx_http_geoip2_module 模块4

如果你是用 Ubuntu/Debian 源里的 Nginx,可以直接安装 libnginx-mod-http-geoip2 包。但我用的是 nginx.org 源里的 Nginx 包,只能自己手工编译了。

编译动态模块需要同时获得 Nginx 源码和模块源码:

git clone https://github.com/leev/ngx_http_geoip2_module
wget http://nginx.org/download/nginx-VERSION.tar.gz
tar zxvf nginx-VERSION.tar.gz
cd nginx-VERSION

./configure --with-compat --add-dynamic-module=../ngx_http_geoip2_module
make modules

编译后的 so 文件位置是objs/ngx_http_geoip2_module.so把它复制到 Nginx 的动态模块目录后再指定加载就可以了。加载指令:

load_module modules/ngx_http_geoip2_module.so;

加载之后就可以配置读取各 GeoLite2 数据:

http {
    # 国家信息
    geoip2 /var/lib/GeoIP/GeoLite2-Country.mmdb {
        auto_reload 2h;
        # 国家代码,如 CN
        $geoip2_data_country_code country iso_code;
        # 国家名字,如 China
        $geoip2_data_country_name country names en;
    }

    # 城市信息
    geoip2 /var/lib/GeoIP/GeoLite2-City.mmdb {
        auto_reload 2h;
        # 名字
        $geoip2_data_city_name city names en;
        # 纬度
        $geoip2_data_city_latitude location latitude;
        # 经度
        $geoip2_data_city_longitude location longitude;
    }

    # 网络信息
    geoip2 /var/lib/GeoIP/GeoLite2-ASN.mmdb {
        auto_reload 2h;
        # ASN 编号
        $geoip2_data_asn autonomous_system_number;
        # ASN 所属组织名称
        $geoip2_data_org autonomous_system_organization;
    }
}

以上是通过 geoip2 加载对应数据库文件,并为相关的信息指定变量。我们在后面的站点配置中可以引用这些变量。上面只用了一部分字段,更多字段请参考 MaxMind 的官方文档5

这是 ip.zz.ac/more 的配置,它额外返回了 ASN 和国家编码:

location /more {
    return 200 '$remote_addr AS$geoip2_data_asn $geoip2_data_country_code\n';
}

这是 ip.zz.ac/json 的配置,它用 json 格式返回了更多字段:

location /json {
    add_header Content-Type "application/json; charset=utf-8";
    add_header Access-Control-Allow-Origin "*";

    return 200 '{
        "ip": "$remote_addr",
        "country": "$geoip2_data_country_code",
        "city": "$geoip2_data_city_name",
        "latitude": "$geoip2_data_city_latitude",
        "longitude": "$geoip2_data_city_longitude",
        "asn": "$geoip2_data_asn",
        "organization": "$geoip2_data_org"
    }\n';
}

大家可以结合文档和 AI 自由定制字段。

到现在为止我只能查询自己的公网 IP 地址。有时候还需要查询指定 IP 地址的相关信息。比如我们希望支持 curl -L ip.zz.ac/json?ip=1.1.1.1 返回 1.1.1.1的信息。这就需要为 ngx_http_geoip2_module 模块指定查询变量。

我们可以使用 map 指定来动态设置查询变量。

http {
    map $arg_ip $ip_lookup_addr {
        ""      $remote_addr;
        default $arg_ip;
    }
}

这里会自动提取 HTTP 请求中的 ip 查询参数并保存在$ip_lookup_addr变量中。如果没有 ip 参数或者参数为空,则自动使用客户端的公网 IP 地址。

接着让 ngx_http_geoip2_module 在查询的时候使用$ip_lookup_addr的值就可以了。

geoip2 /var/lib/GeoIP/GeoLite2-Country.mmdb {
    $geoip2_data_country_code source=$ip_lookup_addr country iso_code;
}

注意这里的source=$ip_lookup_addr参数。

ip.zz.ac 服务已经上线,请放心使用,因为我自己就在用它,出问题会及时修复。另外,为了节约服务器资源,该站点不记录访问日志,大家基本不用担心隐私问题。本文就到这里,希望能对大家有所启发🍻


  1. ZZ.AC 域名已经支持 DDNS,请参见 ./ddns.html↩︎

  2. https://www.maxmind.com/en/create-account↩︎

  3. https://docs.nginx.com/nginx/admin-guide/dynamic-modules/geoip2/↩︎

  4. https://github.com/leev/ngx_http_geoip2_module↩︎

  5. https://dev.maxmind.com/geoip/docs/databases/city-and-country/↩︎