Networking

IP Geolocation

IP geolocation is the process of determining the geographic location of an internet-connected device using its IP address, providing information like country, city, latitude/longitude, ISP, and connection type.

What is IP Geolocation?

IP geolocation is the mapping of an IP address to its real-world geographic location. By analyzing IP address ranges, routing data, and databases maintained by Regional Internet Registries (RIRs) and commercial services, you can determine where a device is physically located, what ISP it uses, and whether it’s a proxy, VPN, or datacenter IP.

How IP Geolocation Works

Lookup Process

interface IPGeolocationProcess {
  step1: {
    action: 'Capture IP address';
    source: 'HTTP request, network connection';
    example: '203.0.113.45';
  };
  step2: {
    action: 'Query geolocation database';
    providers: ['MaxMind GeoIP2', 'IP2Location', 'IPinfo', 'ipapi'];
    data: 'IP ranges mapped to locations';
  };
  step3: {
    action: 'Return location data';
    fields: ['Country', 'Region', 'City', 'Latitude', 'Longitude', 'ISP', 'ASN'];
  };
  step4: {
    action: 'Enrich with additional data';
    optional: ['Timezone', 'Currency', 'Connection type', 'Proxy detection'];
  };
}

Geolocation Data Structure

Typical Response Format

interface IPGeolocationData {
  ip: string;
  version: 'IPv4' | 'IPv6';
  country: {
    code: string; // ISO 3166-1 alpha-2
    name: string;
    flag: string; // Emoji flag
  };
  region: {
    code: string; // State/province code
    name: string;
  };
  city: string;
  postal: string;
  coordinates: {
    latitude: number;
    longitude: number;
    accuracy: number; // Radius in km
  };
  timezone: {
    name: string; // IANA timezone
    offset: number; // UTC offset in seconds
    current_time: string;
  };
  isp: {
    name: string;
    asn: number; // Autonomous System Number
    organization: string;
  };
  connection: {
    type: 'residential' | 'business' | 'cellular' | 'datacenter';
    domain: string;
  };
  security: {
    is_proxy: boolean;
    is_vpn: boolean;
    is_tor: boolean;
    is_hosting: boolean;
    threat_level: 'low' | 'medium' | 'high';
  };
}

IP Geolocation APIs

Using Free APIs

class IPGeolocation {
  async lookupIP(ip?: string): Promise<IPGeolocationData> {
    // Using ipapi.co (free tier: 1000 requests/day)
    const targetIP = ip || 'me'; // 'me' = your current IP

    const response = await fetch(`https://ipapi.co/${targetIP}/json/`);

    if (!response.ok) {
      throw new Error(`Geolocation API error: ${response.status}`);
    }

    const data = await response.json();

    return this.formatResponse(data);
  }

  async lookupIPWithProxy(ip: string, apiKey: string): Promise<IPGeolocationData> {
    // Lookup IP through CORS proxy
    const response = await fetch(
      `https://corsproxy.io/?url=${encodeURIComponent(`https://ipapi.co/${ip}/json/`)}`,
      {
        headers: {
          'x-cors-api-key': apiKey
        }
      }
    );

    const data = await response.json();
    return this.formatResponse(data);
  }

  private formatResponse(data: any): IPGeolocationData {
    return {
      ip: data.ip,
      version: data.version || 'IPv4',
      country: {
        code: data.country_code,
        name: data.country_name,
        flag: this.getCountryFlag(data.country_code)
      },
      region: {
        code: data.region_code,
        name: data.region
      },
      city: data.city,
      postal: data.postal,
      coordinates: {
        latitude: data.latitude,
        longitude: data.longitude,
        accuracy: data.accuracy || 50
      },
      timezone: {
        name: data.timezone,
        offset: 0, // Not provided by ipapi.co
        current_time: data.utc_offset
      },
      isp: {
        name: data.org || 'Unknown',
        asn: parseInt(data.asn?.replace('AS', '')) || 0,
        organization: data.org || 'Unknown'
      },
      connection: {
        type: this.detectConnectionType(data),
        domain: ''
      },
      security: {
        is_proxy: false,
        is_vpn: false,
        is_tor: false,
        is_hosting: data.org?.toLowerCase().includes('hosting') || false,
        threat_level: 'low'
      }
    };
  }

  private detectConnectionType(data: any): IPGeolocationData['connection']['type'] {
    const org = data.org?.toLowerCase() || '';

    if (org.includes('cellular') || org.includes('mobile')) {
      return 'cellular';
    } else if (org.includes('business') || org.includes('corporate')) {
      return 'business';
    } else if (org.includes('hosting') || org.includes('cloud')) {
      return 'datacenter';
    }

    return 'residential';
  }

  private getCountryFlag(countryCode: string): string {
    // Convert country code to flag emoji
    const codePoints = countryCode
      .toUpperCase()
      .split('')
      .map(char => 127397 + char.charCodeAt(0));

    return String.fromCodePoint(...codePoints);
  }

  async getCurrentLocation(): Promise<IPGeolocationData> {
    return this.lookupIP();
  }

  async bulkLookup(ips: string[]): Promise<IPGeolocationData[]> {
    const results: IPGeolocationData[] = [];

    for (const ip of ips) {
      const data = await this.lookupIP(ip);
      results.push(data);

      // Rate limiting: wait 1 second between requests
      await new Promise(resolve => setTimeout(resolve, 1000));
    }

    return results;
  }
}

// Usage
const geolocator = new IPGeolocation();

// Lookup specific IP
const location = await geolocator.lookupIP('8.8.8.8');
console.log(`Location: ${location.city}, ${location.country.name} ${location.country.flag}`);
console.log(`Coordinates: ${location.coordinates.latitude}, ${location.coordinates.longitude}`);
console.log(`ISP: ${location.isp.name}`);

// Lookup current IP
const myLocation = await geolocator.getCurrentLocation();
console.log('My location:', myLocation);

Advanced Geolocation with MaxMind

GeoIP2 Precision

interface MaxMindGeoIP2 {
  service: 'MaxMind GeoIP2 Precision';
  accuracy: 'City-level (99.8% country, 90% city)';
  pricing: '$0.0003 - $0.001 per lookup';
  features: ['Proxy detection', 'Connection type', 'Domain', 'Anonymous IP'];
}

class MaxMindGeolocation {
  private accountId: string;
  private licenseKey: string;

  constructor(accountId: string, licenseKey: string) {
    this.accountId = accountId;
    this.licenseKey = licenseKey;
  }

  async lookupIP(ip: string): Promise<IPGeolocationData> {
    const auth = Buffer.from(`${this.accountId}:${this.licenseKey}`).toString('base64');

    const response = await fetch(
      `https://geoip.maxmind.com/geoip/v2.1/city/${ip}`,
      {
        headers: {
          'Authorization': `Basic ${auth}`
        }
      }
    );

    if (!response.ok) {
      throw new Error(`MaxMind API error: ${response.status}`);
    }

    const data = await response.json();

    return {
      ip: data.traits.ip_address,
      version: data.traits.ip_address.includes(':') ? 'IPv6' : 'IPv4',
      country: {
        code: data.country.iso_code,
        name: data.country.names.en,
        flag: this.getCountryFlag(data.country.iso_code)
      },
      region: {
        code: data.subdivisions?.[0]?.iso_code || '',
        name: data.subdivisions?.[0]?.names?.en || ''
      },
      city: data.city?.names?.en || '',
      postal: data.postal?.code || '',
      coordinates: {
        latitude: data.location.latitude,
        longitude: data.location.longitude,
        accuracy: data.location.accuracy_radius
      },
      timezone: {
        name: data.location.time_zone,
        offset: 0,
        current_time: ''
      },
      isp: {
        name: data.traits.isp || 'Unknown',
        asn: data.traits.autonomous_system_number || 0,
        organization: data.traits.autonomous_system_organization || ''
      },
      connection: {
        type: data.traits.connection_type || 'residential',
        domain: data.traits.domain || ''
      },
      security: {
        is_proxy: data.traits.is_anonymous_proxy || false,
        is_vpn: data.traits.is_anonymous_vpn || false,
        is_tor: data.traits.is_tor_exit_node || false,
        is_hosting: data.traits.is_hosting_provider || false,
        threat_level: this.calculateThreatLevel(data.traits)
      }
    };
  }

  private calculateThreatLevel(traits: any): 'low' | 'medium' | 'high' {
    let score = 0;

    if (traits.is_anonymous_proxy) score += 2;
    if (traits.is_anonymous_vpn) score += 2;
    if (traits.is_tor_exit_node) score += 3;
    if (traits.is_hosting_provider) score += 1;

    if (score >= 5) return 'high';
    if (score >= 3) return 'medium';
    return 'low';
  }

  private getCountryFlag(countryCode: string): string {
    const codePoints = countryCode
      .toUpperCase()
      .split('')
      .map(char => 127397 + char.charCodeAt(0));

    return String.fromCodePoint(...codePoints);
  }
}

// Usage
const maxmind = new MaxMindGeolocation('account_id', 'license_key');
const location = await maxmind.lookupIP('8.8.8.8');
console.log('MaxMind location:', location);

Geolocation-Based Access Control

Geo-Blocking Implementation

class GeoAccessControl {
  private allowedCountries: Set<string>;
  private blockedCountries: Set<string>;
  private geolocator: IPGeolocation;

  constructor() {
    this.allowedCountries = new Set();
    this.blockedCountries = new Set();
    this.geolocator = new IPGeolocation();
  }

  allowCountries(countries: string[]): void {
    countries.forEach(country => this.allowedCountries.add(country.toUpperCase()));
  }

  blockCountries(countries: string[]): void {
    countries.forEach(country => this.blockedCountries.add(country.toUpperCase()));
  }

  async isAllowed(ip: string): Promise<{
    allowed: boolean;
    reason: string;
    location: IPGeolocationData;
  }> {
    const location = await this.geolocator.lookupIP(ip);
    const countryCode = location.country.code.toUpperCase();

    // Check blocked list first
    if (this.blockedCountries.has(countryCode)) {
      return {
        allowed: false,
        reason: `Country ${location.country.name} is blocked`,
        location
      };
    }

    // Check allowed list (if specified)
    if (this.allowedCountries.size > 0 && !this.allowedCountries.has(countryCode)) {
      return {
        allowed: false,
        reason: `Country ${location.country.name} is not in allowed list`,
        location
      };
    }

    // Check for proxy/VPN
    if (location.security.is_proxy || location.security.is_vpn) {
      return {
        allowed: false,
        reason: 'Proxy or VPN detected',
        location
      };
    }

    return {
      allowed: true,
      reason: 'Access granted',
      location
    };
  }

  async middleware(req: any, res: any, next: any): Promise<void> {
    const ip = req.headers['x-forwarded-for']?.split(',')[0] || req.socket.remoteAddress;

    const result = await this.isAllowed(ip);

    if (!result.allowed) {
      res.status(403).json({
        error: 'Access Denied',
        reason: result.reason,
        location: {
          country: result.location.country.name,
          city: result.location.city
        }
      });
      return;
    }

    // Attach location to request
    (req as any).geolocation = result.location;
    next();
  }
}

// Usage - Express middleware
import express from 'express';

const app = express();
const geoControl = new GeoAccessControl();

// Allow only US, UK, CA
geoControl.allowCountries(['US', 'UK', 'CA']);

// Block specific countries
geoControl.blockCountries(['CN', 'RU']);

// Apply geo-blocking middleware
app.use(async (req, res, next) => {
  await geoControl.middleware(req, res, next);
});

app.get('/api/data', (req, res) => {
  const location = (req as any).geolocation;
  res.json({
    message: 'Access granted',
    your_location: `${location.city}, ${location.country.name}`
  });
});

Proxy Detection

Identifying Proxy/VPN IPs

interface ProxyDetection {
  datacenter: {
    method: 'Check ASN against known hosting providers';
    indicators: ['AWS', 'DigitalOcean', 'OVH', 'Hetzner'];
    accuracy: '99%';
  };
  residential: {
    method: 'Behavioral analysis and database lookup';
    indicators: ['Suspicious patterns', 'Known proxy IPs'];
    accuracy: '85%';
  };
  vpn: {
    method: 'Known VPN provider IP ranges';
    indicators: ['NordVPN', 'ExpressVPN', 'ProtonVPN'];
    accuracy: '95%';
  };
  tor: {
    method: 'Tor exit node list';
    source: 'https://check.torproject.org/exit-addresses';
    accuracy: '100%';
  };
}

class ProxyDetector {
  private geolocator: IPGeolocation;

  constructor() {
    this.geolocator = new IPGeolocation();
  }

  async detectProxy(ip: string): Promise<{
    is_proxy: boolean;
    proxy_type: string | null;
    confidence: number;
    details: any;
  }> {
    const location = await this.geolocator.lookupIP(ip);

    let isProxy = false;
    let proxyType: string | null = null;
    let confidence = 0;

    // Check if ISP is known hosting provider
    const hostingKeywords = ['hosting', 'cloud', 'server', 'datacenter', 'vps'];
    const isHosting = hostingKeywords.some(keyword =>
      location.isp.name.toLowerCase().includes(keyword)
    );

    if (isHosting) {
      isProxy = true;
      proxyType = 'datacenter';
      confidence = 0.99;
    }

    // Check known VPN providers
    const vpnKeywords = ['vpn', 'proxy', 'private', 'secure'];
    const isVPN = vpnKeywords.some(keyword =>
      location.isp.name.toLowerCase().includes(keyword)
    );

    if (isVPN) {
      isProxy = true;
      proxyType = 'vpn';
      confidence = 0.95;
    }

    // Check security flags
    if (location.security.is_proxy || location.security.is_vpn) {
      isProxy = true;
      proxyType = location.security.is_vpn ? 'vpn' : 'proxy';
      confidence = 0.90;
    }

    if (location.security.is_tor) {
      isProxy = true;
      proxyType = 'tor';
      confidence = 1.0;
    }

    return {
      is_proxy: isProxy,
      proxy_type: proxyType,
      confidence,
      details: location
    };
  }

  async checkIPReputation(ip: string): Promise<{
    safe: boolean;
    threat_score: number;
    reasons: string[];
  }> {
    const detection = await this.detectProxy(ip);
    const reasons: string[] = [];
    let threatScore = 0;

    if (detection.is_proxy) {
      reasons.push(`Detected as ${detection.proxy_type}`);
      threatScore += 50;
    }

    if (detection.details.security.is_hosting) {
      reasons.push('Datacenter IP');
      threatScore += 30;
    }

    if (detection.details.security.is_tor) {
      reasons.push('Tor exit node');
      threatScore += 70;
    }

    return {
      safe: threatScore < 50,
      threat_score: threatScore,
      reasons
    };
  }
}

// Usage
const detector = new ProxyDetector();

const proxyCheck = await detector.detectProxy('8.8.8.8');
console.log('Proxy detection:', proxyCheck);

const reputation = await detector.checkIPReputation('8.8.8.8');
console.log('IP reputation:', reputation);

Distance Calculation

Geographic Distance Between IPs

class GeoDistance {
  private geolocator: IPGeolocation;

  constructor() {
    this.geolocator = new IPGeolocation();
  }

  haversineDistance(
    lat1: number,
    lon1: number,
    lat2: number,
    lon2: number
  ): number {
    const R = 6371; // Earth radius in km

    const dLat = this.toRadians(lat2 - lat1);
    const dLon = this.toRadians(lon2 - lon1);

    const a =
      Math.sin(dLat / 2) * Math.sin(dLat / 2) +
      Math.cos(this.toRadians(lat1)) *
      Math.cos(this.toRadians(lat2)) *
      Math.sin(dLon / 2) *
      Math.sin(dLon / 2);

    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return R * c;
  }

  private toRadians(degrees: number): number {
    return degrees * (Math.PI / 180);
  }

  async distanceBetweenIPs(ip1: string, ip2: string): Promise<{
    distance_km: number;
    distance_mi: number;
    location1: string;
    location2: string;
  }> {
    const loc1 = await this.geolocator.lookupIP(ip1);
    const loc2 = await this.geolocator.lookupIP(ip2);

    const distanceKm = this.haversineDistance(
      loc1.coordinates.latitude,
      loc1.coordinates.longitude,
      loc2.coordinates.latitude,
      loc2.coordinates.longitude
    );

    return {
      distance_km: Math.round(distanceKm),
      distance_mi: Math.round(distanceKm * 0.621371),
      location1: `${loc1.city}, ${loc1.country.name}`,
      location2: `${loc2.city}, ${loc2.country.name}`
    };
  }
}

// Usage
const geoDistance = new GeoDistance();
const distance = await geoDistance.distanceBetweenIPs('8.8.8.8', '1.1.1.1');
console.log(`Distance: ${distance.distance_km} km (${distance.distance_mi} miles)`);
console.log(`From ${distance.location1} to ${distance.location2}`);

Best Practices

Geolocation Guidelines

interface GeolocationBestPractices {
  accuracy: {
    rule: 'Never rely on city-level accuracy for critical decisions';
    reason: 'Geolocation has ~50km accuracy radius';
    alternative: 'Use country-level for most applications';
  };
  privacy: {
    rule: 'Store geolocation data securely and compliantly';
    reason: 'Geolocation is personal data under GDPR';
    requirements: ['User consent', 'Data retention limits', 'Secure storage'];
  };
  caching: {
    rule: 'Cache geolocation results to reduce API costs';
    duration: '24 hours for most IPs';
    exception: 'Mobile IPs change frequently';
  };
  fallback: {
    rule: 'Always have fallback for geolocation failures';
    reason: 'API rate limits, network errors';
    fallback: 'Default to country-level or no restriction';
  };
}

Learn More

Create a free Account to fix CORS Errors in Production

Say goodbye to CORS errors and get back to building great web applications. It's free!

CORSPROXY Dashboard

Related Terms

More in Networking

Related guides

Back to Glossary