Detecting VPNs and proxies with IP data — what actually works
VPN and proxy detection comes up constantly in fraud prevention, content licensing, and abuse mitigation discussions, and it's one of the most oversold capabilities in the IP data space. This article covers what IP-based detection can actually tell you, what it can't, and how to build detection logic that doesn't waste effort chasing false confidence.
What "VPN detection" actually means
There's no definitive signal that says "this connection is a VPN." What exists is a set of indicators that correlate with VPN/proxy usage, each with real false-positive and false-negative rates. Good detection combines several of these signals and produces a confidence level, not a yes/no answer.
The indicators, roughly in order of reliability:
- Known VPN/hosting provider ASNs — the IP belongs to an organization that's a VPN provider or a datacenter, not a residential/mobile ISP
- Datacenter hosting ranges — even without VPN-specific labeling, an IP registered to AWS, DigitalOcean, OVH, etc. is not a home user
- Behavioral signals — port scanning patterns, unusual request timing, shared IP with many simultaneous sessions
- Commercial VPN detection databases — curated lists mapping known VPN exit nodes (these require paid subscriptions and go stale quickly as providers rotate IPs)
For most applications without a paid detection service, you're working primarily with #1 and #2 — ASN and organization data.
Building basic detection with ASN/ISP data
IPPubblico.org returns the ISP name and ASN for any IP, which is the foundation for this kind of check:
curl "https://ippubblico.org/?api=1"
{
"status": "ok",
"ip": "203.0.113.42",
"isp": "NordVPN",
"asn": "AS205016",
"geo": { "country": "Netherlands", "country_code": "NL" }
}
import requests
KNOWN_VPN_KEYWORDS = [
'vpn', 'nordvpn', 'expressvpn', 'surfshark', 'privateinternetaccess',
'protonvpn', 'cyberghost', 'mullvad', 'tunnelbear', 'ipvanish',
'windscribe', 'hidemyass', 'purevpn',
]
KNOWN_DATACENTER_KEYWORDS = [
'amazon', 'aws', 'google cloud', 'microsoft azure', 'digitalocean',
'ovh', 'hetzner', 'linode', 'vultr', 'scaleway', 'oracle cloud',
'alibaba cloud', 'tencent cloud',
]
def check_ip_type(ip: str = None) -> dict:
url = 'https://ippubblico.org/?api=1'
if ip:
url += f'&ip={ip}'
data = requests.get(url, timeout=5).json()
isp = data.get('isp', '').lower()
is_known_vpn = any(kw in isp for kw in KNOWN_VPN_KEYWORDS)
is_datacenter = any(kw in isp for kw in KNOWN_DATACENTER_KEYWORDS)
return {
'ip': data.get('ip'),
'isp': data.get('isp'),
'is_known_vpn_provider': is_known_vpn,
'is_datacenter': is_datacenter,
'confidence': 'high' if is_known_vpn else ('medium' if is_datacenter else 'low'),
}
This catches the obvious cases — someone using a well-known commercial VPN service, or traffic originating from a cloud server rather than a residential connection. It will miss VPN providers using residential IP pools (a growing and deliberately harder-to-detect category) and won't catch anyone running their own VPN on a home connection.
What this approach cannot detect
Be explicit about the limitations, both to yourself and in any documentation for whoever uses this logic downstream:
Residential proxy networks — services that route traffic through real residential IPs (often via consent-based apps or, less legitimately, compromised devices) are specifically designed to look identical to genuine home users. ASN-based detection has no signal here at all; the ISP genuinely is a residential provider.
Self-hosted VPNs — someone running their own WireGuard/OpenVPN server on a rented VPS will show up as "datacenter IP" (medium confidence) but with no way to distinguish "this person runs a personal VPN" from "this person runs a legitimate cloud service that happens to make API calls."
New or small VPN providers — any keyword-matching list is only as good as its maintenance. New providers, white-label VPN services, and rebranded resellers won't match known keywords until someone updates the list.
Mobile carrier NAT — as covered in earlier articles, CGNAT means many genuine users share IPs with carrier infrastructure that can superficially resemble proxy-like shared-IP patterns.
A more complete detection function
Combining multiple signals into a single confidence score, rather than a binary flag:
import requests
def assess_connection_risk(ip: str = None) -> dict:
url = 'https://ippubblico.org/?api=1'
if ip:
url += f'&ip={ip}'
data = requests.get(url, timeout=5).json()
isp = data.get('isp', '').lower()
asn = data.get('asn', '')
signals = []
if any(kw in isp for kw in KNOWN_VPN_KEYWORDS):
signals.append(('known_vpn_provider', 40))
if any(kw in isp for kw in KNOWN_DATACENTER_KEYWORDS):
signals.append(('datacenter_hosting', 25))
if 'hosting' in isp or 'server' in isp or 'colo' in isp:
signals.append(('generic_hosting_keyword', 15))
risk_score = sum(weight for _, weight in signals)
risk_score = min(risk_score, 100)
return {
'ip': data.get('ip'),
'isp': data.get('isp'),
'asn': asn,
'signals_triggered': [name for name, _ in signals],
'risk_score': risk_score,
'recommendation': (
'block' if risk_score >= 60 else
'flag_for_review' if risk_score >= 25 else
'allow'
),
}
The scoring weights here are illustrative, not authoritative — calibrate them against your own false-positive tolerance. A payment fraud system might weight datacenter detection heavily; a content licensing check might only care about known commercial VPN providers.
Where this fits in a larger system
IP-based VPN/proxy detection should never be the sole gate for any consequential decision. Reasonable places to use it:
- One signal among several in a fraud score, combined with account history, payment method verification, behavioral analysis
- Soft friction, not hard blocking — an extra verification step (CAPTCHA, email confirmation) rather than an outright block
- Logging and monitoring — flagging sessions for review without acting on them automatically
- Rate limiting adjustments — datacenter/VPN traffic might reasonably get a stricter rate limit without being blocked outright
Where it becomes a problem: using ASN-based VPN detection as a hard, silent block for account creation, checkout, or content access. This guarantees false positives against legitimate VPN users (a large and growing population using VPNs for entirely mundane privacy reasons) and does nothing against the residential-proxy services actively built to evade exactly this kind of check.
Testing your own detection logic
Before deploying VPN detection logic, test it against IPs you know the ground truth for — connect through a VPN yourself and check what the API returns:
# Connect via VPN, then check what IPPubblico sees
curl "https://ippubblico.org/?api=1" | python3 -m json.tool
Compare the isp and asn fields against what you'd expect. This is also a good way to verify that your own detection keyword list actually matches real-world ISP name formatting, which varies more than you'd expect ("NordVPN S.A." vs "NORDVPN" vs "M247 Ltd (NordVPN)" are all things you might see for the same provider).
Quick reference
| Signal | Reliability | Catches |
|---|---|---|
| Known VPN provider keyword match | High confidence, low coverage | Major commercial VPN services only |
| Datacenter/cloud ASN | Medium confidence | Any non-residential hosting, including legitimate use |
| Residential proxy networks | Not detectable via ASN | Nothing — indistinguishable from real residential |
| Self-hosted VPN on a VPS | Appears as generic datacenter | Cannot distinguish from other cloud traffic |
Conclusion
ASN and ISP data from a service like IPPubblico gives you a real, useful signal for VPN/proxy detection — but it's a signal with clear and specific blind spots, not a definitive check. The organizations actively trying to evade detection (residential proxy networks in particular) are specifically engineered to defeat exactly this method. Use it as one input in a broader risk assessment, weight it honestly against its real false-positive and false-negative rates, and never let it alone make a hard blocking decision.
Full documentation: ippubblico.org/docs.html
Try it yourself: ippubblico.org
Built VPN detection into a production system? Curious what false-positive rate you settled for as acceptable.
Top comments (0)