Kamero

VPN and Proxy Detection: How They Affect IP Geolocation

VPN usage has grown significantly โ€” estimates suggest 30-40% of internet users use a VPN at least occasionally. For developers relying on IP geolocation, this means a meaningful chunk of your traffic will show incorrect locations. Here's what you need to know.

How VPNs Change Your IP Location

When a user connects through a VPN, their traffic is routed through a server in another location. The website sees the VPN server's IP address, not the user's real one. A user in Tokyo connected to a US VPN server will appear to be in the United States.

Types of IP Masking

MethodHow It WorksDetection Difficulty
Commercial VPNRoutes through VPN provider serversModerate โ€” known IP ranges
Corporate VPNRoutes through company gatewayHard โ€” looks like normal traffic
HTTP ProxyIntermediary forwards requestsEasy โ€” often leaks headers
SOCKS ProxyLower-level proxy protocolModerate
TorMulti-hop relay networkEasy โ€” known exit nodes
Residential ProxyRoutes through real home IPsVery hard

Detection Techniques

1. Timezone Mismatch

Compare the IP-based timezone with the browser's timezone:

async function detectTimezoneAnomaly() {
  const { timezone: ipTimezone } = await fetch(
    "https://geo.kamero.ai/api/geo"
  ).then(r => r.json());

  const browserTimezone = Intl.DateTimeFormat()
    .resolvedOptions().timeZone;

  if (ipTimezone && browserTimezone !== ipTimezone) {
    return {
      anomaly: true,
      ipTimezone,
      browserTimezone,
      // Could be VPN, or user manually changed TZ
    };
  }

  return { anomaly: false };
}

2. Language Mismatch

async function detectLanguageAnomaly() {
  const { country } = await fetch(
    "https://geo.kamero.ai/api/geo"
  ).then(r => r.json());

  const browserLang = navigator.language.split("-")[0];

  // Expected primary languages by country
  const expected = {
    JP: "ja", DE: "de", FR: "fr", BR: "pt",
    CN: "zh", KR: "ko", RU: "ru", IT: "it",
  };

  const expectedLang = expected[country];
  if (expectedLang && browserLang !== expectedLang && browserLang !== "en") {
    return { anomaly: true, country, browserLang, expectedLang };
  }

  return { anomaly: false };
}

3. WebRTC Leak Detection

Some VPNs leak the real IP through WebRTC. While this is a privacy concern for users, it can be a detection signal:

// Note: Modern VPNs and browsers increasingly block this
async function getWebRTCIPs(): Promise<string[]> {
  return new Promise((resolve) => {
    const ips: string[] = [];
    const pc = new RTCPeerConnection({
      iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
    });

    pc.createDataChannel("");
    pc.createOffer().then(offer => pc.setLocalDescription(offer));

    pc.onicecandidate = (event) => {
      if (!event.candidate) {
        pc.close();
        resolve(ips);
        return;
      }
      const match = event.candidate.candidate.match(
        /([0-9]{1,3}(\.[0-9]{1,3}){3})/
      );
      if (match && !match[1].startsWith("192.168")
          && !match[1].startsWith("10.")) {
        ips.push(match[1]);
      }
    };

    setTimeout(() => { pc.close(); resolve(ips); }, 3000);
  });
}

How to Handle VPN Users

The right approach depends on your use case:

For Personalization (Content, Currency, Language)

Accept the VPN location gracefully. If someone is using a US VPN, showing them USD pricing is fine โ€” they chose that location.

For Analytics

Flag potential VPN traffic but still count it. Note the anomaly in your data so you can filter if needed.

For Fraud Prevention

VPN usage is a risk signal, not proof of fraud. Add it to your risk score but don't auto-block:

async function assessVPNRisk() {
  const geo = await fetch("https://geo.kamero.ai/api/geo")
    .then(r => r.json());

  let riskScore = 0;
  const signals = [];

  // Timezone check
  const browserTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
  if (geo.timezone && browserTz !== geo.timezone) {
    riskScore += 15;
    signals.push("timezone_mismatch");
  }

  // Language check
  const lang = navigator.language.split("-")[0];
  const langMap = { JP: "ja", DE: "de", FR: "fr", CN: "zh" };
  if (langMap[geo.country] && lang !== langMap[geo.country]) {
    riskScore += 10;
    signals.push("language_mismatch");
  }

  return { riskScore, signals, geo };
}

For Compliance / Geo-Blocking

If you legally must block certain regions, VPN bypass is a known limitation. Document that you use IP-based blocking and that VPN circumvention is the user's responsibility. For strict compliance, combine with additional verification (phone number, ID, payment method country).

Key Takeaways

Get IP Location Data

Returns timezone for mismatch detection. Free, no key required.

View Documentation โ†’