IPASIS - IP Reputation and Risk Intelligence API
Blog/Security Engineering

The Engineering Complexity of Accurate VPN and Proxy Detection

February 09, 202612 min read

Updated September 3, 2026

Building an in-house solution to detect anonymizers is often the first instinct for engineering teams facing fraud or scraping attacks. However, the landscape of IP obfuscation has evolved significantly beyond static datacenters. This post explores why simple blocklists fail and the technical overhead required to maintain high-fidelity detection.

1. The Volatility of IPv4 Reputation

The primary challenge is the churn rate of IP ownership. Static lists of "bad IPs" degrade in value within hours, not days.

  • Cloud Recycling: AWS, GCP, and Azure recycle elastic IPs aggressively. An IP used by a VPN provider today might be assigned to a legitimate SaaS webhook tomorrow.
  • DHCP Churn: ISP users (residential) are frequently reassigned IPs. A permanent ban on a dynamically assigned IP will eventually result in false positives against legitimate users.

The failure mode here is asymmetric, and that asymmetry is what makes it dangerous. When a stale list blocks a real customer you do not get an alert; you get a support ticket weeks later, or more often nothing at all, because the user simply left. When it misses a fraudster you find out in chargebacks. A list that is quietly wrong in both directions still looks healthy on every dashboard you have.

2. Residential Proxies and the ASN Illusion

Traditional detection relies on flagging Autonomous System Numbers (ASNs) associated with hosting providers (e.g., DigitalOcean, Hetzner). This logic fails against Residential Proxies (ResIPs).

ResIP networks route traffic through compromised IoT devices or users who have opted into SDKs (like free VPN apps) in exchange for service. The traffic exits through a legitimate ISP connection (e.g., Comcast, AT&T).

The Engineering Challenge: You cannot simply block the ASN. You must distinguish between a NAT gateway servicing a family and a NAT gateway servicing a proxy node.

Conceptual Detection Logic (Python)

To detect ResIPs, you need to correlate the IP against known proxy subnets or analyze open ports indicative of proxy services, rather than just ASN types.

def assess_risk(ip_metadata):
    """
    Simple heuristics fail against Residential Proxies.
    A manual implementation requires real-time port scanning data.
    """
    # 1. Check ASN Type
    is_hosting = ip_metadata.get('asn_type') == 'hosting'
    
    # 2. Check for Proxy Ports (SOCKS5, HTTP Proxy)
    # This data is hard to acquire without active probing (honeypots)
    open_proxy_ports = ip_metadata.get('open_ports', [])
    has_proxy_signature = any(p in [1080, 8080, 3128] for p in open_proxy_ports)

    if is_hosting:
        return "High Risk (Datacenter)"
    elif has_proxy_signature:
        # This detects the Residential Proxy
        return "High Risk (Residential Proxy)"
    
    return "Low Risk"

Why ASN Type Alone Misclassifies

The subtler problem is that ASN type and traffic type are two different facts, and teams routinely treat them as one. An ASN is registered once and described broadly; the traffic leaving it changes daily. Three cases break the equivalence:

  • Hosting ASN, legitimate traffic. Server-side integrations, corporate egress gateways, CI runners and webhook senders all originate from hosting ASNs. Blocking hosting wholesale blocks your own customers' backends.
  • ISP ASN, anonymized traffic. This is the residential-proxy case, and it is the entire point of the product category: the ASN says "consumer broadband" precisely because that is what the operator paid for.
  • Mixed ASNs. Many networks carry consumer broadband, business fibre and hosting under one number. There is no single correct label for the ASN, so any single label you assign is wrong for part of the traffic.

The practical consequence is that ASN type is a prior, not a verdict. It should shift a score, never decide one.

3. TCP/IP Stack Fingerprinting

Sophisticated detection goes beyond the IP address. It involves Passive OS Fingerprinting (p0f). VPNs often alter the TCP/IP packet header parameters (TTL, Window Size, MSS) in ways that create a mismatch with the User-Agent header sent by the browser.

  • MTU Mismatches: Tunneling protocols (OpenVPN, WireGuard) introduce overhead, often lowering the Maximum Transmission Unit (MTU). If a request claims to be from a standard Chrome browser on Windows but the MSS suggests an MTU of 1300, it is likely encapsulated.
  • OS Mismatch: If the User-Agent says "iPhone" but the TCP signature looks like Linux (often used by proxy servers), the request is synthetic.

The operational catch is where this data lives. TCP options are consumed at the edge: by the time a request reaches your application it has usually traversed a CDN, a load balancer and a TLS terminator, each of which re-originates the connection. The fingerprint you want was destroyed three hops before your handler ran. Capturing it means instrumenting the terminating layer itself, which is a different and considerably less pleasant engineering project than the one most teams think they are starting.

4. CGNAT and False Positives

Carrier-Grade NAT (CGNAT) is prevalent in mobile networks. A single public IPv4 address may represent thousands of legitimate users.

Naive implementation of rate-limiting or IP banning on CGNAT ranges results in massive collateral damage. Accurate detection requires maintaining a database of mobile gateway ranges to apply looser blocking rules, forcing reliance on device fingerprinting rather than IP reputation for these segments.

Disambiguating CGNAT from a Residential Proxy

Both look identical in the shape most teams measure: one address, many unrelated sessions. Volume alone cannot separate them, and rate limits tuned on volume will punish an entire mobile carrier to catch one proxy node. The signals that do separate them are qualitative:

  • Carrier gateways are enumerable and stable. Mobile operators announce their gateway ranges, and those ranges persist. A proxy pool is neither published nor stable; its whole value is rotation.
  • The session mix differs. Traffic behind a carrier NAT is heterogeneous — many device profiles, many app versions, ordinary diurnal rhythm. Proxy-node traffic is unusually homogeneous in some dimension (identical automation fingerprints, uniform timing) or impossibly heterogeneous in another (dozens of accounts, no shared device history).
  • Geo-velocity resolves the ambiguity. A carrier gateway serves a coherent metro area. A residential proxy exit is selected by a buyer who wanted that country, so sessions on it jump between unrelated locations at speeds no human travels.

The rule that follows: never apply an IP-level block to a known CGNAT range. Route those segments to device- or account-level controls instead, and reserve address-level action for infrastructure you can positively identify.

5. Why In-Band Port Probing Is Impractical

The conceptual snippet above quietly assumes you have open_ports. Acquiring it inside a live request is where in-house builds usually stall. Three independent problems, any one of which is disqualifying:

  • Latency. A TCP connect scan across even a handful of candidate ports costs hundreds of milliseconds against a responsive host, and hits your full timeout against a filtered one — because a dropped SYN gives you no answer, only a wait. That is being spent inside a decision that should complete in single-digit milliseconds, on the signup path, synchronously.
  • It does not answer the question. Proxy software binds to ephemeral, rotating ports; the well-known 1080/8080/3128 triple mostly finds misconfigured hosts, not commercial proxy infrastructure. Worse, under CGNAT and behind consumer routers the address you are scanning is a carrier gateway, so a positive result may belong to an entirely different subscriber than the one who sent the request.
  • It is abuse. Unsolicited scanning of consumer addresses generates ISP complaints and can put your egress ranges on blocklists — the same kind of list you are trying to consume. You end up degrading your own reputation to assess someone else's.

This is the actual reason the signal is bought rather than computed: it must be gathered continuously and out-of-band, from infrastructure whose purpose is gathering it, and then served from memory at request time.

6. The Standing Cost of the Feed

Teams size this project by the detection logic, which is the small part. The recurring cost is the data pipeline behind it:

  • Acquisition and refresh. Proxy pools rotate continuously, so the dataset is never finished. A refresh cadence measured in days is already producing stale verdicts.
  • Distribution. A useful lookup happens in memory at request time, which means the compiled dataset has to reach every instance in every region you serve, on every rebuild, without a cold-start cliff on deploy.
  • False-positive regression. Every feed update is a silent change to your production risk decisions. Without a standing validation job that re-checks known-good addresses after each build, a bad upstream day becomes blocked customers you never hear about.
  • The signals are separate datasets. Geolocation, ASN attribution, privacy-network classification and abuse contacts come from different sources with different licences and refresh rates. Assembling them into one coherent record is most of the work.

None of this is intellectually hard. It is simply permanent, and it is why the build-versus-buy calculation rarely survives contact with the second quarter.

7. Querying the Signals Instead

The alternative is to treat classification as a lookup. A single request returns the assembled record, and your rule logic operates on fields rather than on a scanning infrastructure:

curl -s "https://api.ipasis.com/v1/lookup?ip=185.220.101.1" \
  -H "X-API-Key: <your_api_key>"

The response below is a real one for a Tor exit node, and it demonstrates the ASN illusion from section 2 concretely:

{
  "ip": "185.220.101.1",
  "country": "DE",
  "asn": {
    "ASN": "AS60729",
    "Name": "Stiftung Erneuerbare Freiheit",
    "Route": "185.220.101.0/24",
    "Type": "vpn"
  },
  "privacy": {
    "VPN": true,
    "Proxy": false,
    "Tor": true,
    "Relay": false,
    "Hosting": false,
    "Abuse": true,
    "Service": "Tor",
    "Type": "Residential"
  }
}

Note what an ASN-only rule would have concluded. privacy.Hosting is false — this address is not datacenter infrastructure, so a "block hosting ASNs" rule misses it entirely. The classification that matters, privacy.Tor, is an independent fact about the address, not an inference from its network. Note also privacy.Type reading Residential on an address that is simultaneously a Tor exit: exactly the overlap section 2 describes.

Gating on those fields is then ordinary application logic:

import requests

ENDPOINT = "https://api.ipasis.com/v1/lookup"

def classify(ip: str, api_key: str) -> str:
    r = requests.get(
        ENDPOINT,
        params={"ip": ip},
        headers={"X-API-Key": api_key},
        timeout=2,
    )
    r.raise_for_status()
    privacy = r.json().get("privacy", {})

    # Deny outright only for classes you can identify with confidence.
    if privacy.get("Tor") or privacy.get("Abuse"):
        return "deny"

    # Anonymized but frequently legitimate: step up, do not block.
    if privacy.get("VPN") or privacy.get("Proxy"):
        return "step_up"

    # Server-side origin. Expected for API clients, suspect for a signup form.
    if privacy.get("Hosting"):
        return "review"

    # iCloud Private Relay and similar: real users behind a privacy network.
    if privacy.get("Relay"):
        return "allow"

    return "allow"

Two properties of this shape are worth stating explicitly, because they are where in-house implementations tend to go wrong. First, it is graded — step_up exists so that VPN users, who are overwhelmingly ordinary privacy-conscious customers, meet friction rather than a wall. Second, it fails open: the timeout is short and a lookup failure should let the request through to your other controls, because an availability problem in a risk service must never become an outage in your signup flow.

If you need the email address weighed alongside the IP in a single verdict, /v1/validate-email returns a combined 0–100 score with a risk.recommendation of ALLOW, REVIEW or BLOCK, so the grading above arrives precomputed.

FAQ

Q: How do you detect a residential proxy? A: Not from the ASN — a residential proxy exits through a real consumer ISP. Detection correlates three independent signal families: membership in known proxy-network egress pools (harvested continuously, since the pools rotate), behavioural signals such as one address carrying many unrelated sessions or impossible geo-velocity, and TCP/IP stack mismatches against the advertised User-Agent. Any one alone produces false positives; confidence comes from agreement between them.

Q: Why do VPN blocklists fail? A: Because they encode a fact with a short half-life. Cloud providers recycle elastic IPs, so today's VPN exit is tomorrow's customer webhook sender. Residential addresses churn through DHCP independently. A static list only gets less accurate after generation, and its errors are silent — you see blocked requests, never the legitimate users who gave up.

Q: Can you detect a proxy from the IP address alone? A: For datacenter and commercial VPN traffic, largely yes: the address belongs to enumerable infrastructure. For residential proxies, not in principle — the address genuinely belongs to a real household, and the only distinguishing fact is that someone else is routing through it. Establishing that requires observing the address over time, which is why the signal is bought rather than derived.

Q: How accurate is VPN detection? A: Accuracy varies by traffic class rather than being one number. Commercial VPN and datacenter egress is the reliable end. Residential proxy and CGNAT-fronted mobile traffic is the hard end, and anyone quoting near-perfect accuracy there is quoting a benchmark rather than production. Treat detection as a graded signal — allow, step up, deny — and reserve the aggressive action for classes you identify confidently.

Q: Can't I just use a GeoIP database? A: No. GeoIP tells you where an IP is, not what it is. A VPN server in New York looks geographically identical to a legitimate user in New York.

Q: How do we handle iCloud Private Relay? A: Apple publishes the egress ranges for Private Relay. These should generally be treated as "low trust" but not necessarily malicious, depending on your risk tolerance. They are technically proxies but authenticate valid Apple users, and a large share are ordinary iPhone customers — blocking the range removes real people.

Q: Why not active probing? A: Active probing (scanning the incoming IP for open proxy ports) adds latency to the user request and can trigger abuse complaints from ISPs. It also answers the wrong question, since proxy software uses ephemeral ports and CGNAT means the scanned host is often not the one that sent the request. Passive analysis via a dataset provider is faster and safer — see section 5.


Scale Your Security with IPASIS

Maintaining a real-time database of millions of proxy nodes, calculating TCP deviations, and tracking residential churn is a dedicated infrastructure challenge.

IPASIS provides a single, low-latency API endpoint to detect VPNs, proxies, and TOR nodes with high fidelity. Stop building blocklists and start analyzing intelligence. The free tier covers 3,000 requests per month, which is enough to run the classifier above against real signup traffic before committing to anything.

Related references: the VPN provider directory maps commercial VPNs to their hosting ASNs, proxy IP ranges covers residential, datacenter and mobile pools, and combined IP + email scoring returns the graded recommendation described in section 7.

Get your API Key | View Documentation

Start detecting VPNs and Bots today.

Identify anonymized traffic instantly with IPASIS. Free tier: 3,000 requests per month.

Get API Key