TRACIO's IP intelligence subsystem analyzes every visitor's IP address server-side. It identifies VPNs, proxies, Tor usage, and datacenter origins, and resolves city-level geolocation. All analysis runs server-side with no client-side dependencies.
IP intelligence reaches your application through two surfaces:
network (booleans + connection type) and geo (country, city, coordinates, timezone, ISP). See Webhooks for the full payload shape and signature verification.ip, ip_country, ip_city, ip_latitude, ip_longitude, ip_timezone). The VPN/proxy/Tor/datacenter flags (is_vpn, is_proxy, is_tor, is_datacenter) are present in the read-API JSON too, but the read query does not populate them — they always serialize as false. The populated flags arrive via the webhook network object.VPN detection uses four independent methods. Each method addresses a different VPN evasion technique.
Compares the visitor's device timezone (from JavaScript Intl.DateTimeFormat) against the expected timezone from GeoIP lookup on the connecting IP.
Device timezone: Europe/Prague (from browser signal s9)GeoIP timezone: America/New_York (from IP geolocation)Result: timezoneMismatch = trueThis method also tracks a 7-day rolling mismatch rate per IP. If 50% or more of requests from an IP show timezone mismatches, the IP is flagged even when individual requests appear consistent.
Strengths: Catches most consumer VPNs. Works regardless of VPN protocol.
Limitations: Same-timezone VPN servers (e.g., Prague user on Berlin VPN) may not trigger. Users traveling legitimately may cause false positives.
Matches the visitor's IP against a database of 42+ known VPN provider ASNs:
| Provider | ASN |
|---|---|
| NordVPN | AS212238 |
| ExpressVPN | AS394711 |
| Mullvad | AS198385 |
| ProtonVPN | AS209103 |
| M247 (VPN infrastructure) | AS9009 |
| IPVanish | AS206092 |
| CyberGhost | AS46562 |
| Private Internet Access | AS395954 |
| Surfshark | AS212238 (shared with NordVPN) |
| AtlasVPN | AS212238 (shared with NordVPN) |
Strengths: Zero false positives for known providers.
Limitations: Cannot detect corporate VPNs or self-hosted VPN servers.
Identifies anonymizing relay services:
Uses a WebRTC TURN server to discover the visitor's real IP:
RTCPeerConnection with TRACIO's TURN serverStrengths: Bypasses VPNs that do not proxy WebRTC traffic.
Limitations: Some VPNs (Mullvad, browser extensions) block WebRTC entirely. Safari Lockdown Mode blocks WebRTC.
VPN usage is delivered as the boolean network.vpn on the webhook payload. The
individual detection methods above are internal inputs — they are not exposed as
a per-method breakdown or a confidence score. The connection type, when known,
is surfaced as network.connectionType.
{ "network": { "vpn": true, "proxy": false, "tor": false, "datacenter": false, "connectionType": "wifi" }}// `payload` is the webhook delivery body (/docs/webhooks)if (payload.network.vpn) { // visitor is connecting through a VPN}TRACIO detects both datacenter and residential proxies.
Identified by:
Via, X-Forwarded-For, X-Proxy-ID, Forwarded)Residential proxies are harder to detect because they use legitimate ISP-assigned IPs. TRACIO uses three indicators:
Known residential proxy ASNs: BrightData/Luminati (AS9009, AS56030), Oxylabs (AS44724), SmartProxy (AS200019), Webshare (AS209242), GeoSurf (AS398101)
High cardinality: More than 30 unique visitors per hour from a single IP suggests proxy traffic
UA diversity: More than 5 distinct User-Agent strings from a single IP within an hour suggests multiple real users routing through one proxy
Detection scoring: ASN match = 0.6, high cardinality = 0.3, UA diversity = 0.2. Threshold: 0.3 or higher triggers detection.
Datacenter and residential proxy detection both surface as the boolean
network.proxy. Datacenter origin is reported separately as network.datacenter.
The webhook contract exposes booleans only — there is no proxy-type subclassification
or confidence score in the public payload.
{ "network": { "vpn": false, "proxy": true, "tor": false, "datacenter": true, "connectionType": "wired" }}// `payload` is the webhook delivery body (/docs/webhooks)if (payload.network.proxy) { // visitor is connecting through a proxy}TRACIO maintains an up-to-date list of Tor exit node IPs sourced from the Tor Project's bulk exit list (check.torproject.org/torbulkexitlist), supplemented by a static fallback list of 56 well-known exit nodes.
The list is refreshed periodically and stored in a thread-safe in-memory map. IP lookups are O(1) via hash map.
A Tor exit-node match surfaces as the boolean network.tor.
{ "network": { "vpn": false, "proxy": false, "tor": true, "datacenter": false }}// `payload` is the webhook delivery body (/docs/webhooks)if (payload.network.tor) { // visitor's IP is a known Tor exit node}City-level geolocation is resolved server-side from the connecting IP and is exposed on both delivery surfaces.
geo)| Field | Type | Description |
|---|---|---|
geo.country | string | ISO 3166-1 alpha-2 country code |
geo.city | string | City name |
geo.lat | number | Approximate latitude |
geo.lon | number | Approximate longitude |
geo.timezone | string | IANA timezone identifier |
geo.isp | string | Internet service provider name |
{ "geo": { "country": "CZ", "city": "Prague", "lat": 50.05, "lon": 14.4, "timezone": "Europe/Prague", "isp": "Comcast Cable" }}The event returned by the Server API carries geolocation
as flat fields alongside the raw ip:
| Field | Type | Description |
|---|---|---|
ip | string | Connecting IP address |
ip_country | string | ISO 3166-1 alpha-2 country code |
ip_city | string | City name |
ip_latitude | number | Approximate latitude |
ip_longitude | number | Approximate longitude |
ip_timezone | string | IANA timezone identifier |
Datacenter origin is surfaced as the boolean network.datacenter on the webhook
payload. IPs are checked against static CIDR ranges for major cloud providers,
covering AWS, Google Cloud, Azure, Oracle Cloud, Alibaba Cloud, DigitalOcean,
Hetzner, OVH, Linode/Akamai, and Vultr.
IP-blocklist matching and velocity (cross-IP / cross-country activity) tracking
are not part of the current public contract. Neither the webhook payload nor
the read-API event exposes blocklist categories or velocity intervals. The
webhook surface for network risk is limited to the boolean network flags and
the connection type; use those together with the decision and bot fields
(see Smart Signals) for risk decisions.
This reads the webhook payload's network object directly. See
Webhooks for the full payload and signature verification.
// `payload` is the webhook delivery body (/docs/webhooks)function assessIPRisk(payload: WebhookPayload) { const { vpn, proxy, tor, datacenter } = payload.network
const risks: string[] = [] let riskLevel = "low"
if (tor) { risks.push("Tor exit node") riskLevel = "critical" }
if (vpn) { risks.push("VPN detected") riskLevel = riskLevel === "low" ? "medium" : riskLevel }
if (proxy) { risks.push("Proxy detected") riskLevel = riskLevel === "low" ? "medium" : riskLevel }
if (datacenter) { risks.push("Datacenter IP") riskLevel = riskLevel === "low" ? "medium" : riskLevel }
return { riskLevel, risks }}