ci框架本身不提供ip归属地解析能力,必须依赖外部api(如ipapi.co)或本地ip库;$this->input->ip_address()常不准是因为仅读取$_server['remote_addr'],在代理、cdn等环境下返回的是中间节点ip,需手动解析x-forwarded-for等请求头获取真实ip。

直接说结论:CI 框架本身不提供 IP 归属地解析能力,必须靠外部 HTTP API 或本地 IP 库;用 ip-api.com 或 ipapi.co 是最省事的选择,但要注意调用限制和真实 IP 获取是否准确。
为什么 $this->input->ip_address() 返回的 IP 经常不准
CI 的 $this->input->ip_address() 默认只读 $_SERVER['REMOTE_ADDR'],在有 Nginx、CDN、云平台(如腾讯云、阿里云、PagodaBox)或反向代理时,这个值通常是中间节点的 IP,不是用户真实出口 IP。
必须手动适配请求头才能拿到真实 IP:
- 检查
$_SERVER['HTTP_X_FORWARDED_FOR'],取逗号分隔后的第一个非unknown值 - fallback 到
$_SERVER['HTTP_X_REAL_IP'](Nginx 常用) - 最后才用
$_SERVER['REMOTE_ADDR'] - CI4 中可改写
App\Config\Filters.php的ip_address配置,或自定义一个getRealIP()辅助函数
用 ipapi.co 查询归属地(推荐新手)
它返回结构清晰的 JSON,无需 API Key,IPv4/IPv6 都支持,免费限 1000 次/天。注意:不要在前端直接调用,避免暴露频率限制或被刷。
CI4 中可在 Model 或 Library 里封装:
public function getIpLocation(string $ip): ?array
{
$url = 'https://ipapi.co/' . urlencode($ip) . '/json/';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 || !$response) {
return null;
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return null;
}
return [
'country' => $data['country_name'] ?? '',
'region' => $data['region_name'] ?? '',
'city' => $data['city'] ?? '',
'ip' => $data['ip'] ?? $ip,
'timezone'=> $data['timezone'] ?? ''
];
}
关键点:
- 必须设
CURLOPT_TIMEOUT,否则慢响应会拖垮整个请求 - 检查
CURLINFO_HTTP_CODE,429 Too Many Requests说明触发限频,需加缓存或降频 - 字段名以实际返回为准:
country_name不是country,region_name不是province
CI 中存储和去重 IP 地理信息要防重复插入
别每次请求都查 API 再入库。典型错误是:用户刷新页面 → 查 IP → 调 API → 插数据库 → 又刷新 → 又插一条相同 IP 记录。
正确做法是先查库是否存在该 IP:
- 用
$this->db->where('ip', ip2long($ip))->get('ip_locations')->getRow()查 - 没命中再调 API + 插入;命中则直接返回缓存结果
- 建议给
ip字段加唯一索引:ALTER TABLE ip_locations ADD UNIQUE KEY uk_ip (ip); - 如果想保留历史变更(比如 IP 所属城市迁移),就不要唯一约束,改用时间戳 + 最新标记字段
本地 IP 库(如 qqwry.dat)在 CI 里难维护
纯真库虽离线可用,但在 CI 环境下容易踩坑:
-
qqwry.dat是二进制格式,不能用file_get_contents+json_decode处理 - CI 的 autoloader 不自动加载自定义解析类,得手动
require_once或注册命名空间 - IPv6 完全不支持,而现代 CDN 和移动网络大量使用 IPv6
- 更新麻烦:需要定期下载新版本、校验文件完整性、处理 Windows 自动解压损坏问题
除非你有强合规要求(如完全禁止外网出站请求)、且只服务国内 IPv4 用户,否则不建议在 CI 项目中硬上本地库。
真实项目里,90% 的场景用 ipapi.co 或 ip-api.com 加一层数据库缓存,已经足够稳;最难的从来不是“怎么查”,而是“怎么确保查的是真实 IP”和“怎么不让 API 调用把服务拖垮”。











