在应用里接入 IP 地理位置查询
用 JavaScript / Node.js / Python / Go 调用 ipconf API,并合理处理缓存、VPN 用户与隐私规则。
IP 地理位置能查到什么,查不到什么
IP 地理位置基于地址分配数据库,把 IP 映射到可能的国家、地区、城市。国家级一般 >95% 准确,地区级 75-90%,城市级 50-80%(视国家而定)。
它查不到街道地址、姓名或身份。对 VPN/代理用户、企业统一出口、移动 CGN、所有云/VPS IP 都会偏差。产品要做到:位置对了能用,位置错了也不阻塞用户。
选对接口
`/api/ip` 返回调用方自己的位置,适合"展示本地内容"。`/api/ip/{ip}` 查任意 IP,适合日志分析、风控、显示"此次登录来自 X"。
加 `?lang=zh` 返回中文地名。默认英文。两个接口返回相同 schema。
// 浏览器查当前访问者:
fetch("https://api.ipconf.me/api/ip")
.then(r => r.json())
.then(data => console.log(data.country, data.city));
// 查指定 IP:
fetch("https://api.ipconf.me/api/ip/8.8.8.8?lang=zh")
.then(r => r.json())
.then(data => console.log(data)); 前端调用 vs 后端调用
浏览器调用用的是浏览器的 IP——查当前访问者通常这样就够。但 API URL 会暴露给每个访客,还受广告拦截和 CORS 影响。
后端调用默认用服务器自己的出口 IP,要查访客的就得显式把 IP 当路径参数传。后端调用能缓存、重试、统一处理错误。
// Node.js:从请求里取访客 IP,再去查:
app.get("/local-content", async (req, res) => {
const visitorIp = req.headers["cf-connecting-ip"]
|| req.headers["x-forwarded-for"]?.split(",")[0]
|| req.ip;
const r = await fetch(`https://api.ipconf.me/api/ip/${visitorIp}`);
const geo = await r.json();
res.json({ visitor: visitorIp, country: geo.country });
}); 缓存策略
家宽用户的 IP-位置映射在分钟到小时尺度上稳定;固定基础设施 IP 在小时到天尺度上稳定。基本不会有"一分钟内查同一个 IP 两次还需要重查"的需求。
进程内 LRU 缓存(10-60 分钟 TTL)是最简单的起点。多机部署加 Redis。不要永久缓存——IP 分配会变,旧数据会让真实用户看到错的内容。
// Node.js + 简易 LRU + TTL:
import { LRUCache } from "lru-cache";
const cache = new LRUCache({ max: 10000, ttl: 30 * 60 * 1000 });
async function geo(ip) {
if (cache.has(ip)) return cache.get(ip);
const r = await fetch(`https://api.ipconf.me/api/ip/${ip}`);
const data = await r.json();
cache.set(ip, data);
return data;
} 处理 VPN、代理、CDN 用户
可观比例的流量是 VPN/代理用户。他们的位置是 VPN 出口节点,不是真实位置。对这类用户有影响的功能(区域限制内容、地区定价、"附近门店")一定要给显式的手动覆盖入口——别让用户因为定位错被永久卡住。
在 Cloudflare 等 CDN 后面,源站看到的是边缘节点 IP。要读 `CF-Connecting-IP` 或 `X-Forwarded-For` 后再去查,否则每个访客都"在旧金山"。
// Python(Flask 示例):
from flask import request
import requests
def visitor_geo():
ip = (
request.headers.get("CF-Connecting-IP")
or request.headers.get("X-Forwarded-For", "").split(",")[0].strip()
or request.remote_addr
)
r = requests.get(f"https://api.ipconf.me/api/ip/{ip}", timeout=2)
return r.json() if r.ok else None 隐私与 GDPR
在欧盟和不少其他司法管辖区,IP 与其他数据结合即构成个人数据。查询本身一般没问题,把结果绑定到用户账户长期保存就要重新评估了。
实操原则:只保存业务真需要的字段;不要无限期持久化精确查询结果;隐私政策里说明这项处理;为依赖地理位置的功能提供关闭入口。把 geo 当作软提示,不当作身份断言。
其他语言示例
接口就是 HTTP + JSON,任何语言都能用。下面给 Python 和 Go 的最小示例。超时、重试、缓存按前面几节的模式自行添加。
# Python(requests):
import requests
r = requests.get("https://api.ipconf.me/api/ip/1.1.1.1", timeout=2)
print(r.json()["country"])
# Go(net/http):
package main
import (
"encoding/json"
"net/http"
"fmt"
)
func main() {
r, _ := http.Get("https://api.ipconf.me/api/ip/1.1.1.1")
defer r.Body.Close()
var data map[string]any
json.NewDecoder(r.Body).Decode(&data)
fmt.Println(data["country"])
}