PHP获取服务器公网IP只能通过调用外部API(如api.ipify.org)或解析网卡地址(需过滤私有IP),$_SERVER['SERVER_ADDR']不可靠;ipip.net的ipdb库是离线地理定位最优选,支持IPv6且原生中文,优于需扩展和授权的MaxMind方案。

PHP 怎么拿到服务器本机的公网 IP(不是 127.0.0.1 或内网)
直接读 $_SERVER['SERVER_ADDR'] 多数情况下返回的是监听地址,比如 0.0.0.0、127.0.0.1 或内网 IP(如 192.168.x.x),并不能反映真实对外 IP。真正能用的只有两种方式:
- 调用外部 HTTP 接口(如
https://api.ipify.org或国内可用的https://myip.ipip.net),用file_get_contents()或curl获取返回值,注意加超时和错误处理 - 从网卡设备读取(Linux 下可用
exec('hostname -I')或解析/sys/class/net/下的inet地址),但需确保 PHP 有执行权限,且多网卡时要过滤掉127.0.0.1和私有地址段(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)
不推荐依赖 $_SERVER['REMOTE_ADDR']——那是客户端 IP,不是本机。
用 ipip.net 免费库做地域识别(纯离线、无 API 调用)
ipip.net 提供的 ipdb 格式数据库(如 ipipfree.ipdb)是目前 PHP 离线定位最轻量可靠的方案,比老牌 qqwry.dat 更新勤、结构清晰、支持 IPv6。
- 下载地址:官方 GitHub 发布页(搜
ipipnet/ipdb-php),选最新.ipdb文件 - 安装扩展:
composer require ipip/ipdb - 基础用法示例:
$db = new \Ipip\IPDB('/path/to/ipipfree.ipdb');
$result = $db->find('202.102.110.204', 'CN'); // 返回数组,含 country, province, city 等键
// 注意:第二个参数是语言代码,'CN' 表示中文字段;用 'EN' 则返回英文
首次加载 .ipdb 文件会有毫秒级开销,建议在常驻进程(如 Swoole)中复用实例,或用 opcache 加速反序列化。
立即学习“PHP免费学习笔记(深入)”;
为什么不用 GeoLite2 + MaxMind DB(常见踩坑点)
MaxMind 官方的 GeoLite2-Country.mmdb 确实权威,但 PHP 使用它有几个硬伤:
- 必须装
maxminddb扩展(非纯 PHP 实现),部分共享主机不支持 - 免费版自 2022 年起需注册账号+API key 才能下载,且每月限次;离线使用也得填 license key 生成下载链接
-
geoip2/geoip2Composer 包默认走在线查询,若没配好本地 DB 路径,会静默 fallback 到 HTTP 请求,导致超时或暴露 IP - 中文字段需额外映射(DB 里只有英文),不如 ipip.net 原生支持
'CN'输出
除非项目已深度绑定 MaxMind 生态,否则没必要绕路。
IPv6 地址怎么查?要注意什么
ipip.net 的 .ipdb 支持 IPv6,但 PHP 中传入前必须标准化:
- 不能直接用
$_SERVER['REMOTE_ADDR']原样传——IPv6 地址可能带方括号(如[2001:db8::1])或压缩写法(::1) - 先用
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)校验,再用inet_pton()+inet_ntop()归一化(去掉::简写,补全为完整 128 位) - 某些 CDN(如 Cloudflare)会把 IPv6 客户端地址转成 IPv4 格式(
100::开头的 IPv6-mapped IPv4),这种要提前识别并跳过地理库查询,否则结果错乱
真实环境里,IPv6 查询失败往往不是库问题,而是输入格式没规整——这点比 IPv4 容易忽略得多。











