noise, apply user overrides, and restrict geolocation to default selection rather than access control. The following implementation demonstrates a production-ready resolver in TypeScript.
Step 1: Asynchronous Fetch with Timeout and Fallback
Network calls to geolocation endpoints must never block request processing. A timeout prevents cascading latency, while a fallback ensures continuity during provider outages or rate limits.
import { LocationResult, GeoApiResponse } from './types';
const GEO_API_ENDPOINT = 'https://ippubblico.org/?api=1';
const DEFAULT_FALLBACK_COUNTRY = 'US';
const REQUEST_TIMEOUT_MS = 2500;
async function resolveLocationFromNetwork(): Promise<LocationResult> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
try {
const response = await fetch(GEO_API_ENDPOINT, {
signal: controller.signal,
headers: { 'Accept': 'application/json' }
});
if (!response.ok) {
throw new Error(`Geo API returned ${response.status}`);
}
const payload: GeoApiResponse = await response.json();
return {
country: payload.geo?.country_code ?? DEFAULT_FALLBACK_COUNTRY,
city: payload.geo?.city ?? null,
coordinates: payload.geo?.lat && payload.geo?.lon
? { lat: payload.geo.lat, lon: payload.geo.lon }
: null,
source: 'network',
confidence: 'medium'
};
} catch {
return {
country: DEFAULT_FALLBACK_COUNTRY,
city: null,
coordinates: null,
source: 'fallback',
confidence: 'low'
};
} finally {
clearTimeout(timeoutId);
}
}
Why this matters: The AbortController pattern guarantees the request terminates within the defined window. Returning a structured LocationResult with explicit source and confidence fields allows downstream logic to adjust behavior based on data reliability.
Step 2: Infrastructure Noise Filtering
Cloud providers, VPN services, and proxy networks introduce systematic location distortion. Filtering by ASN and ISP metadata prevents infrastructure traffic from polluting user location logic.
const INFRASTRUCTURE_ASN_PREFIXES = new Set([
'AS16509', 'AS14618', // AWS
'AS15169', 'AS36040', // Google Cloud
'AS8075', 'AS8070', // Microsoft Azure
'AS14061', 'AS20473', // DigitalOcean, Vultr
'AS24940', 'AS62041' // Hetzner, OVH
]);
const VPN_KEYWORDS = ['vpn', 'proxy', 'tunnel', 'nord', 'express', 'mullvad', 'pia'];
const CLOUD_KEYWORDS = ['amazon', 'google cloud', 'microsoft azure', 'digitalocean', 'vultr', 'linode', 'ovh', 'hetzner'];
function classifyTraffic(payload: GeoApiResponse): { isInfrastructure: boolean; reason: string } {
const asn = payload.asn ?? '';
const isp = (payload.isp ?? '').toLowerCase();
if (INFRASTRUCTURE_ASN_PREFIXES.has(asn)) {
return { isInfrastructure: true, reason: 'cloud_provider_asn' };
}
const matchesVpn = VPN_KEYWORDS.some(keyword => isp.includes(keyword));
const matchesCloud = CLOUD_KEYWORDS.some(keyword => isp.includes(keyword));
if (matchesVpn) return { isInfrastructure: true, reason: 'vpn_proxy_detected' };
if (matchesCloud) return { isInfrastructure: true, reason: 'cloud_isp_match' };
return { isInfrastructure: false, reason: 'residential_corporate' };
}
Why this matters: ASN and ISP fields remain stable even when routing changes. Filtering at this layer prevents false location assignments from skewing analytics, personalization, or compliance logging.
Step 3: User Override Integration
IP geolocation should never override explicit user intent. A lightweight override mechanism ensures that manual selections take precedence over network-derived estimates.
function applyUserOverride(detected: LocationResult): LocationResult {
if (typeof window === 'undefined') return detected;
const storedOverride = localStorage.getItem('app_location_override');
if (!storedOverride) return detected;
try {
const override = JSON.parse(storedOverride) as Partial<LocationResult>;
return {
...detected,
country: override.country ?? detected.country,
city: override.city ?? detected.city,
source: 'user_override',
confidence: 'high'
};
} catch {
return detected;
}
}
Why this matters: Users traveling, using corporate networks, or operating behind proxies will frequently encounter mismatched location data. Providing an explicit override path eliminates support tickets and prevents accidental feature gating.
Step 4: Resolution Orchestration
The final resolver chains network fetch, infrastructure filtering, and user override into a single deterministic flow.
export async function getLocation(): Promise<LocationResult> {
const networkData = await resolveLocationFromNetwork();
const payload = await fetch(GEO_API_ENDPOINT).then(r => r.json());
const { isInfrastructure } = classifyTraffic(payload);
if (isInfrastructure) {
return {
...networkData,
source: 'filtered_infrastructure',
confidence: 'low'
};
}
return applyUserOverride(networkData);
}
Architecture Rationale: This design separates concerns: network I/O, metadata classification, and preference management. Each layer can be tested independently, cached selectively, and monitored for mismatch rates. The system degrades gracefully when APIs fail, ASN data is missing, or user overrides are absent.
Pitfall Guide
1. Treating IP Geolocation as Ground Truth
Explanation: IP-to-location mapping is probabilistic. Registry data, BGP routing, and vendor inference introduce inherent variance. Assuming deterministic accuracy leads to false blocks and misrouted content.
Fix: Always tag location data with a confidence score. Route logic based on confidence thresholds, not raw values.
2. Hard-Blocking by Country Code
Explanation: VPNs, CGNAT, and corporate gateways routinely place legitimate users outside their expected jurisdiction. Blocking by IP country will inevitably deny access to travelers, remote workers, and users in restricted regions.
Fix: Use IP location for default selection only. Implement explicit verification flows for restricted features.
Explanation: Country codes alone cannot distinguish residential traffic from cloud egress or proxy networks. Relying solely on country_code fields masks infrastructure noise.
Fix: Parse asn and isp fields on every request. Filter or flag infrastructure traffic before applying location logic.
4. Assuming IPv6 Parity with IPv4
Explanation: IPv6 address space is significantly larger and less densely mapped by commercial vendors. Lookup latency, missing fields, and coordinate variance are higher for IPv6 clients.
Fix: Implement separate fallback chains for IPv6. Treat IPv6 location data as lower confidence until vendor coverage improves.
5. Synchronous or Blocking Geo Lookups
Explanation: Geolocation API calls introduce network latency. Blocking request processing on these calls degrades TTFB and increases timeout rates under load.
Fix: Always use asynchronous fetch with explicit timeouts. Cache results per session or IP range with short TTLs.
6. Over-Reliance on City or Coordinate Data
Explanation: City-level accuracy hovers around 50-60%. Coordinates typically represent city centroids, not precise locations. Mobile CGNAT and corporate routing further distort granularity.
Fix: Restrict city/coordinate usage to non-critical personalization. Never use for compliance, billing, or access control.
7. Skipping Fallback Chains
Explanation: Geolocation providers experience rate limits, outages, and regional routing failures. Systems without fallbacks fail completely when the primary endpoint is unreachable.
Fix: Implement a three-tier fallback: primary API β secondary API β hardcoded default. Log fallback triggers for monitoring.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Content personalization (language, currency) | IP country default + user override | High accuracy at country level, low risk if wrong | Minimal API cost, high UX gain |
| CDN routing or edge selection | IP country/region + ASN filtering | Reduces latency, avoids cloud egress misrouting | Low cost, improves performance |
| Fraud detection signal | IP country + ASN + behavioral heuristics | IP alone is insufficient; combine with velocity, device, and payment data | Moderate API cost, reduces false positives |
| Regulatory compliance logging | IP country + explicit user declaration | Audit trails require verified data; IP serves as secondary reference | Low cost, ensures compliance coverage |
| Access control or feature gating | User-verified location + MFA | IP geolocation cannot guarantee jurisdiction; verification prevents false blocks | Higher implementation cost, eliminates support friction |
Configuration Template
// geo-config.ts
export const GEO_CONFIG = {
endpoints: {
primary: 'https://ippubblico.org/?api=1',
secondary: 'https://ipapi.co/json/',
fallback: 'US'
},
timeouts: {
primary: 2500,
secondary: 3000
},
cache: {
ttlMs: 600000, // 10 minutes
maxEntries: 5000
},
filtering: {
infrastructureAsns: new Set([
'AS16509', 'AS14618', 'AS15169', 'AS36040',
'AS8075', 'AS8070', 'AS14061', 'AS20473'
]),
vpnKeywords: ['vpn', 'proxy', 'tunnel', 'nord', 'express', 'mullvad'],
cloudKeywords: ['amazon', 'google cloud', 'microsoft azure', 'digitalocean', 'vultr', 'linode']
},
enforcement: {
allowIpBlocking: false,
requireUserVerificationForRestricted: true
}
};
Quick Start Guide
- Install dependencies: Add
typescript and @types/node to your project. Create a geo-resolver.ts file and paste the orchestration code from the Core Solution section.
- Configure endpoints: Replace
GEO_API_ENDPOINT with your preferred provider. Ensure the response structure matches the GeoApiResponse interface, or add a lightweight mapper.
- Integrate fallback chain: Implement the timeout wrapper and fallback return. Test with network throttling to verify graceful degradation.
- Add override storage: Wire
localStorage or session storage to the applyUserOverride function. Expose a UI toggle for manual location selection.
- Deploy and monitor: Route location-dependent logic through
getLocation(). Instrument fallback triggers, ASN filter rates, and override usage. Adjust TTLs and timeouts based on observed latency and accuracy metrics.