← Back to list

Stop Making These 5 IP Geolocation Mistakes

IP geolocation accuracy is not a single number. At the country level, commercial providers report 98–99.8% accuracy. At the city level…

Abdul Mateen · 2026-05-23 12:01 · 0 claps · 10.6 min read
#mistakes-to-avoid #common-mistakes #ip-geolocation
Open on Medium ↗

Diagram showing IP geolocation accuracy dropping from country to city to postal level

Diagram showing IP geolocation accuracy dropping from country to city to postal level

Stop Making These 5 IP Geolocation Mistakes

IP geolocation accuracy is not a single number. At the country level, commercial providers report 98–99.8% accuracy. At the city level, that drops to 20–75%, depending on the provider, the country, and the type of IP address. MaxMind, the industry’s most established provider, publishes 66% city-level accuracy within a 50km radius for US IPs. Outside the US, it gets worse.

Most developers never check these numbers. They call an API, get back a city name and coordinates, and build business logic on it as if it were GPS-precise. The result: geo-redirects that send users to the wrong storefront, fraud rules that flag legitimate customers, and analytics dashboards that count Berlin as Chicago.

These are five specific mistakes that cause those failures, and the code and design patterns that prevent each one.

TL;DR

  • City-level IP geolocation is 20–75% accurate, not 99%. Use the confidence radius your API returns before making location-critical decisions.
  • VPN, proxy, and privacy relay traffic returns the server’s location, not the user’s. Check security flags before trusting geolocation data.
  • Half of Google’s traffic now uses IPv6, and CGNAT puts thousands of IPv4 users behind one address. One IP no longer means one user or one location.
  • Caching geolocation results for too long serves stale data. Not caching at all wastes money and adds latency. A 24-hour TTL is a reasonable default.
  • X-Forwarded-For is a client-settable header. Trusting it without validation lets attackers spoof their location to bypass your geo-restrictions.

IP geolocation is a useful signal, not a source of truth. Every implementation should account for its accuracy limits, handle the traffic it cannot locate, and validate the headers it reads. These five fixes address the most common failure points.

Mistake 1: Trusting City-Level Accuracy for Critical Decisions

This is the most widespread mistake because the marketing pages of geolocation providers encourage it. A provider advertises “99% accuracy” without specifying that 99% applies only at the country level. Developers read that number and assume they can route users to city-specific storefronts or trigger city-level compliance rules with the same confidence.

The real numbers tell a different story. MaxMind, the industry’s longest-running provider, reports 99.8% accuracy at the country level, approximately 80% at the state/region level (for US IPs), and 66% at the city level within a 50km radius. Their deeper accuracy page widens that city-level range to 20–75% globally, depending on the country and network type.

Those numbers are not a failure of MaxMind specifically. They reflect a fundamental limitation of IP geolocation. ISPs register IP blocks to their headquarters, not to each subscriber’s address. Mobile carriers share pools of addresses across towers that cover hundreds of kilometers. Dynamic IPs get reassigned across regions. No commercial provider can consistently resolve a city when the underlying data points to a metropolitan area at best.

The fix: use the confidence radius. Providers like MaxMind, ipinfo, and ipgeolocation.io return an accuracy_radius field alongside the latitude and longitude. MaxMind returns it in kilometers. It tells you: the actual location is probably within this circle, not at the center point.

If your API returns a confidence radius, use it to gate your decisions:

const express = require('express');
const app = express();
app.get('/store', async (req, res) => {
  const clientIp = req.ip;
  try {
    const geoResponse = await fetch(
      `https://api.example.com/?ip=${clientIp}`,
      { signal: AbortSignal.timeout(1500) }
    );
    const geo = await geoResponse.json();
    const accuracyKm = geo.accuracy_radius ?? 500;
    if (accuracyKm <= 50) {
      // High confidence: safe to use city-level routing
      return res.redirect(`/store/${geo.country_code}/${geo.city}`);
    }
    // Low confidence: fall back to country-level
    return res.redirect(`/store/${geo.country_code}`);
  } catch (err) {
    // Fail open: default store rather than broken redirect
    return res.redirect('/store/global');
  }
});

The threshold of 50km is not arbitrary. MaxMind uses it as their standard radius for measuring city-level accuracy, and most providers follow a similar convention. Adjust it based on what you’re deciding: a content language preference tolerates a 200km radius. A same-city delivery estimate does not.

If your API does not return a confidence radius, treat city-level data as a suggestion, not a fact. Use it for personalization (showing local weather, pre-selecting a timezone) where a wrong answer is minor. Do not use it for access control, legal compliance, or fraud rules where a wrong answer has consequences.

Mistake 2: Ignoring VPN, Proxy, and Relay Traffic

A user in London connects through NordVPN’s Frankfurt server. Your geolocation API returns Frankfurt. Your fraud system flags the login because the user’s account is registered in the UK but the IP says Germany. You lock the account and send a “suspicious activity” email.

That user was doing nothing wrong. They were just using a VPN, and your system treated a VPN server’s location as the user’s location.

This is not an edge case anymore. Commercial VPN usage has grown substantially over the past five years. Add iCloud Private Relay (enabled by default for Safari users on Apple devices), Cloudflare WARP, corporate VPNs, and datacenter proxies, and you have a traffic segment where IP geolocation returns confident, specific, and completely wrong location data.

Check for VPN/proxy/relay status before trusting the geolocation result. Several IP intelligence APIs return boolean flags or detection scores alongside location data. ipgeolocation.io’s IP Security API, for example, returns separate flags for VPN, proxy, residential proxy, Tor, and relay, plus a provider name where identifiable and a composite threat score from 0 to 100.

The key is to differentiate by type. Not all masked traffic is equally suspicious:

  • Tor exit nodes: High risk for fraud and abuse. Block or challenge.
  • Commercial VPN (NordVPN, ExpressVPN, Mullvad): Medium risk. The user may be privacy-conscious, traveling, or circumventing geo-restrictions. Add friction (MFA, email confirmation), but don’t block outright.
  • iCloud Private Relay, Cloudflare WARP: Low risk. These are legitimate privacy features used by regular consumers. Blocking them means blocking paying customers. Treat the geolocation as approximate and fall back to country-level logic.
  • Residential proxies: Hard to detect, high risk for fraud. These are IP addresses that appear to be normal residential connections but are actually proxied through a third-party network. vpnapi.io, IPstack, AbstractAPI, and ipregistry do not list a dedicated residential proxy flag. Providers that do (ipgeolocation.io, IPQS, Spur) have a significant detection advantage here.

If your geolocation workflow does not check for proxied traffic at all, you are making decisions on data you should not trust for a growing share of your users.

Mistake 3: Assuming One IP Equals One User (and Ignoring IPv6)

Two related assumptions break IP-based logic: that every IP address maps to roughly one user, and that IPv4 is still the default.

The CGNAT problem. Carrier-Grade NAT (defined in RFC 6598) puts thousands of mobile subscribers behind a single public IPv4 address. In India, where mobile carriers serve hundreds of millions of users on limited IPv4 space, a single CGNAT address can represent users spread across an entire state. Rate limiting by IP? You just rate-limited a city block. Fraud scoring by IP reputation? One bad actor poisons the score for thousands of innocent users.

There is no clean fix for CGNAT at the IP layer. The best you can do is recognize it: if the source IP falls within 100.64.0.0/10, it is a CGNAT address. Reduce your confidence in any per-user conclusions drawn from it. Use session-level signals (cookies, device fingerprints, login credentials) instead of IP-level signals for user identification.

The IPv6 gap. Google’s IPv6 adoption tracking shows that IPv6 traffic reached 50% of Google’s total traffic for the first time in March 2026. In countries like India and France, IPv6 carries the majority of internet traffic.

Many developers still test their geolocation integration exclusively with IPv4 addresses. But IPv6 geolocation databases tend to have thinner coverage, especially for newer allocations. If your implementation does not handle IPv6 at all (some older libraries silently fail on IPv6 input), you have a blind spot covering half your potential traffic.

Test your geolocation integration with both 8.8.8.8 (Google's IPv4 DNS) and 2001:4860:4860::8888 (Google's IPv6 DNS). If the IPv6 lookup fails, returns null, or throws an unhandled error, fix it before it reaches production.

Mistake 4: Caching Geolocation Data Wrong

IP-to-location mappings are not permanent. Dynamic IPs get reassigned by ISPs. IP blocks get reallocated between organizations. A geolocation provider updates its database and corrects a mapping that was wrong for months.

Developers make two opposite mistakes here.

Too long: Caching a geolocation result for 30 days (or indefinitely) means serving stale data. A user whose ISP rotated their IP to a different region still gets content for their old location. A customer who triggered a fraud rule because their cached IP resolved to the wrong country keeps getting flagged long after the underlying data was corrected.

Not at all: Making a fresh API call on every HTTP request when the answer changes at most once per day wastes API quota, adds 20–50ms of latency to each request, and costs real money at scale. At 1 million daily page views, an uncached integration burns through mid-tier API plans in days.

The right approach: cache with a TTL matched to the data’s change rate.

const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);

async function getGeoData(ip) {
  const cacheKey = `geo:${ip}`;
  try {
    const cached = await redis.get(cacheKey);
    if (cached) return JSON.parse(cached);
  } catch (err) {
    // Redis down: proceed without cache, don't block the request
    console.warn('Redis read failed, falling through to API', err.message);
  }
  const apiKey = process.env.IPGEO_API_KEY;
  if (!apiKey) throw new Error('Missing IPGEO_API_KEY');
  const response = await fetch(
    `https://api.ipgeolocation.io/v3/ipgeo?apiKey=${apiKey}&ip=${ip}`,
    { signal: AbortSignal.timeout(1500) }
  );
  if (!response.ok) {
    // Fail open: return null rather than crashing
    console.error(`Geolocation API error: ${response.status}`);
    return null;
  }
  const data = await response.json();
  try {
    // 24-hour TTL for geolocation, shorter for security data
    await redis.set(cacheKey, JSON.stringify(data), 'EX', 86400);
  } catch (err) {
    console.warn('Redis write failed', err.message);
  }
  return data;
}

A 24-hour TTL is a reasonable default for geolocation (country, city, timezone, currency). For VPN/proxy detection data, use a shorter TTL (1–4 hours), because a user might disconnect from a VPN mid-session. For country-level data used in content localization, you can safely extend to 7 days.

The important detail: the code above fails open on both Redis errors and API errors. If your geolocation lookup fails and the feature it supports is non-critical (language preference, local weather), serve a default rather than an error page. If it is critical (access control, compliance), fail closed and require the user to verify manually.

Mistake 5: Trusting X-Forwarded-For Without Validation

If your application sits behind a reverse proxy (Nginx, Cloudflare, AWS ALB, or any CDN), the client’s real IP is not in the TCP socket address. It is in the X-Forwarded-For header, set by your proxy.

The problem: X-Forwarded-For is not a protected header. Any HTTP client can set it. An attacker can send a request with X-Forwarded-For: 1.2.3.4 and your application will geolocate 1.2.3.4 instead of the attacker's real IP. If your geo-restriction logic trusts that header, the attacker just bypassed it.

This is not theoretical. IP spoofing via header injection is one of the simplest attacks against geo-restricted content, rate limiters, and fraud detection systems.

The defense: only trust the hop your known proxy added. In Express, this means configuring trust proxy correctly:

const express = require('express');
const app = express();

// WRONG: trusts the entire X-Forwarded-For chain, including client-set values
// app.set('trust proxy', true);
// RIGHT: trust only one proxy hop (your load balancer or CDN)
app.set('trust proxy', 1);
// Now req.ip returns the IP your proxy saw, not what the client claimed
app.get('/api/location', (req, res) => {
  const clientIp = req.ip;
  // This IP came from your proxy, not from the client's header
  res.json({ ip: clientIp });
});

Setting trust proxy to true tells Express to trust every entry in the X-Forwarded-For chain. Setting it to 1 tells Express to trust only the last proxy hop, which is the one your infrastructure controls.

If you run behind Cloudflare, the correct client IP is in the CF-Connecting-IP header, which Cloudflare sets and the client cannot override. AWS ALB uses X-Forwarded-For but only appends (doesn't overwrite), so the rightmost entry is the one ALB set.

The general rule: know how many proxy hops exist between your application and the internet, trust only those hops, and ignore everything the client injected before them.

What to Check in Your Implementation

If you have an existing codebase that uses IP geolocation, ask three questions:

  1. What decisions depend on city-level accuracy? If any of them have real consequences (access control, compliance, fraud), check whether you are validating the confidence radius before acting.
  2. Do you handle VPN/proxy traffic separately? If your geolocation consumer does not check for proxied traffic, it treats all IPs equally. That was acceptable in 2015. In 2026, it means making wrong decisions for a significant share of users.
  3. How does your app extract the client IP? If it reads X-Forwarded-For without configuring proxy trust, an attacker can feed it any IP they want.

If you want to test your integration against a real API that returns both geolocation data and VPN/proxy/relay detection in one call, ipgeolocation.io’s free tier covers 1,000 requests per day with no credit card required.

FAQs

How accurate is IP geolocation at the city level?

City-level accuracy ranges from 20% to 75% depending on the provider, country, and network type. MaxMind publishes 66% city-level accuracy within 50km for US IPs, and 99.8% at the country level. Always check whether your provider publishes accuracy data broken down by granularity level.

Can IP geolocation detect VPN users?

Standard IP geolocation APIs do not detect VPNs. Separate IP intelligence or security APIs detect VPN, proxy, Tor, and relay traffic by matching IPs against known provider ranges, behavioral signals, and threat feeds. Detection quality varies: some providers return only boolean flags, while others return confidence scores and the specific VPN provider name.

Does IP geolocation work with IPv6 addresses?

Most commercial providers support IPv6, but accuracy and coverage tend to be thinner than IPv4, especially for newly allocated ranges. As of March 2026, IPv6 carries roughly half of global internet traffic according to Google’s tracking data. Test your integration with IPv6 addresses specifically before deploying.

How often does IP-to-location data change?

IP-to-location mappings change continuously as ISPs reassign dynamic IPs, organizations acquire or release IP blocks, and providers correct inaccurate records. Commercial geolocation databases update daily or multiple times per day. A 24-hour cache TTL is a reasonable default for geolocation data; shorter (1–4 hours) for security/VPN detection data.

Is X-Forwarded-For safe to use for IP geolocation?

Not without validation. X-Forwarded-For is a client-settable HTTP header. Any request can include a forged value. Only trust the entry added by your known proxy (the rightmost entry for most proxy configurations). In Express, set trust proxy to the number of proxy hops you control, not to true. Behind Cloudflare, prefer the CF-Connecting-IP header.

What is CGNAT and how does it affect IP geolocation?

Carrier-Grade NAT (RFC 6598) places thousands of mobile users behind a single public IPv4 address in the 100.64.0.0/10 range. Geolocation for a CGNAT address reflects the carrier’s NAT gateway, not any individual user’s location. Per-user rate limiting, fraud scoring, or access decisions based on a CGNAT IP affect all users sharing that address.

Should I cache IP geolocation results?

Yes. Uncached lookups waste API quota and add latency on every request. Cache geolocation results with a 24-hour TTL as a default. Use shorter TTLs (1–4 hours) for security detection data, and longer TTLs (up to 7 days) for country-level data that rarely changes. Make sure your cache layer fails gracefully so a Redis outage does not break your application.

What accuracy should I expect for mobile users?

Lower than fixed broadband. Mobile carriers assign IP addresses from pools shared across wide geographic areas, sometimes spanning multiple cities or even states. MaxMind reports that for mobile networks, geolocation typically resolves to a broad region rather than a specific city. Reduce your confidence in city-level data for mobile traffic and fall back to country or region-level decisions.


메타데이터
post_id
5f128bbdc0d6
slug
stop-making-these-5-ip-geolocation-mistakes-5f128bbdc0d6
url
https://medium.com/@mateenabdul993/stop-making-these-5-ip-geolocation-mistakes-5f128bbdc0d6
canonical_url
https://medium.com/@mateenabdul993/stop-making-these-5-ip-geolocation-mistakes-5f128bbdc0d6
author_url
https://medium.com/@mateenabdul993
status
ok
fetched_at
2026-06-09 15:37:30