Step 1: the real client IP
| Setup | Header |
|---|---|
| Cloudflare | CF-Connecting-IP |
| nginx / most load balancers | first value of X-Forwarded-For (or X-Real-IP) |
| No proxy | the socket address |
Only trust these headers if the request really came through your proxy; otherwise clients can fake them.
Node.js
const cache = new Map();
async function geolocate(ip) {
if (cache.has(ip)) return cache.get(ip);
const res = await fetch(`https://api.ipbase.com/v2/info?ip=${encodeURIComponent(ip)}`, {
headers: { apikey: process.env.IPBASE_KEY },
});
const { data } = await res.json();
const result = { country: data.location.country.alpha2, city: data.location.city?.name, isp: data.connection?.isp };
cache.set(ip, result);
return result;
}
Python
import os, requests
from functools import lru_cache
@lru_cache(maxsize=10_000)
def geolocate(ip: str) -> dict:
res = requests.get("https://api.ipbase.com/v2/info", params={"ip": ip},
headers={"apikey": os.environ["IPBASE_KEY"]}, timeout=5)
res.raise_for_status()
loc = res.json()["data"]["location"]
return {"country": loc["country"]["alpha2"], "city": (loc.get("city") or {}).get("name")}
PHP
function geolocate(string $ip): array
{
$ch = curl_init('https://api.ipbase.com/v2/info?ip=' . urlencode($ip));
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['apikey: ' . getenv('IPBASE_KEY')], CURLOPT_TIMEOUT => 5]);
$data = json_decode(curl_exec($ch), true)['data'];
curl_close($ch);
return ['country' => $data['location']['country']['alpha2'], 'city' => $data['location']['city']['name'] ?? null];
}
Tips
- Use the
fieldsparameter to return only what you need:fields=location.country.alpha2,security. - Look up up to 100 addresses at once with the batch endpoint when processing logs.
- Private addresses (10.x, 192.168.x, ::1) have no location; skip them before calling the API.