IPBot
Get Started
Back to Blog
| Security

How to Detect Proxies, VPNs, and Tor Nodes - Technical Guide

Learn how to detect VPNs, proxies, and Tor exit nodes using IP intelligence APIs. Protect your application from fraud and abusive traffic.

SecurityVPN DetectionFraud PreventionAPI

Detecting proxies, VPNs, and Tor nodes is essential for fraud prevention, preventing fake account creation, and blocking abusive traffic. This guide covers detection methods and implementation strategies.

Why Detect Proxies and VPNs?

Fraud Prevention

  • Block suspicious transactions from masked IPs
  • Identify multi-account users bypassing restrictions
  • Prevent stolen credit card usage

Security

  • Block traffic from known bad IPs
  • Prevent brute-force attacks from proxy networks
  • Detect automated bot traffic

Compliance

  • Enforce geographic content restrictions
  • Meet regulatory requirements for user verification
  • Prevent access from sanctioned regions

Detection Methods

1. IP Database Matching

The most reliable method is checking against known proxy/VPN databases:

async function checkProxyStatus(ip) {
const response = await fetch(`https://api.ipbot.com/${ip}`);
const data = await response.json();
return {
isProxy: data.is_proxy || false,
isVPN: data.is_vpn || false,
isTor: data.is_tor || false,
isHosting: data.is_hosting || false,
riskScore: data.risk_score || 0,
};
}

2. Behavior Analysis

Combine IP data with behavioral signals:

function analyzeSuspiciousBehavior(ipData, userBehavior) {
const riskFactors = [];
if (ipData.is_vpn || ipData.is_proxy) {
riskFactors.push("proxy_or_vpn");
}
if (ipData.is_tor) {
riskFactors.push("tor_exit_node");
}
if (ipData.is_hosting) {
riskFactors.push("hosting_provider");
}
if (userBehavior.loginAttempts > 5) {
riskFactors.push("multiple_failed_logins");
}
if (userBehavior.speed < 100) {
// Abnormally fast form filling
riskFactors.push("automated_behavior");
}
return {
riskLevel: riskFactors.length,
factors: riskFactors,
shouldBlock: riskFactors.length >= 2,
};
}

3. TCP/IP Fingerprinting

Advanced detection using connection characteristics:

// Requires server-side implementation
function analyzeTCPFingerprint(connection) {
return {
ttl: connection.ttl, // Time To Live values vary by VPN
tcpOptions: connection.tcpOptions,
windowSize: connection.windowSize,
// Compare against known VPN fingerprints
};
}

Understanding Different Proxy Types

TypeDescriptionDetectability
Transparent ProxyForwards client IP in headersEasy (check headers)
Anonymous ProxyHides client IPMedium (database lookup)
Elite ProxyHides proxy existenceHard (behavior analysis)
VPNEncrypted tunnelMedium (database + timing)
TorMulti-hop routingEasy (known exit nodes)
Data Center IPHosting/Cloud providerEasy (ASN analysis)

Implementation Examples

Basic VPN Detection

async function shouldAllowAccess(ip) {
const ipInfo = await fetch(`https://api.ipbot.com/${ip}`).then((r) =>
r.json(),
);
// Block known threats
if (ipInfo.is_tor || ipInfo.risk_score > 80) {
return false;
}
// Optionally allow VPN with additional verification
if (ipInfo.is_vpn || ipInfo.is_proxy) {
return requireAdditionalVerification();
}
return true;
}

Risk-Based Authentication

async function getAuthRequirement(ip) {
const ipInfo = await fetch(`https://api.ipbot.com/${ip}`).then((r) =>
r.json(),
);
// Calculate risk score
let risk = 0;
if (ipInfo.is_vpn) risk += 30;
if (ipInfo.is_proxy) risk += 40;
if (ipInfo.is_tor) risk += 50;
if (ipInfo.is_hosting) risk += 20;
if (ipInfo.risk_score > 50) risk += ipInfo.risk_score / 2;
// Return auth level based on risk
if (risk >= 80) return "BLOCK";
if (risk >= 50) return "TWO_FACTOR";
if (risk >= 20) return "EMAIL_VERIFICATION";
return "STANDARD";
}

Python Implementation

import requests
from functools import lru_cache
@lru_cache(maxsize=1000)
def get_ip_intel(ip: str, ttl: int = 3600) -> dict:
"""Get IP intelligence with caching"""
response = requests.get(f"https://api.ipbot.com/{ip}")
return response.json()
def should_block_user(ip: str, user_data: dict) -> bool:
"""Determine if user should be blocked"""
intel = get_ip_intel(ip)
# Hard blocks
if intel.get("is_tor"):
return True
if intel.get("risk_score", 0) > 80:
return True
# Soft blocks with conditions
if intel.get("is_vpn") or intel.get("is_proxy"):
# New accounts via VPN get blocked
if user_data.get("account_age_days", 0) < 7:
return True
return False

Detecting Tor Exit Nodes

Tor exit nodes are publicly listed and easily detectable:

async function isTorExitNode(ip) {
const response = await fetch(`https://api.ipbot.com/${ip}`);
const data = await response.json();
// Check multiple indicators
return {
isTor: data.is_tor,
torExit: data.tor_exit,
onionRouting: data.onion_routing,
reason: data.risk_reasons?.includes("tor_exit_node"),
};
}

Handling False Positives

Not all VPN usage is malicious. Consider these scenarios:

Legitimate VPN Use Cases

  • Remote workers accessing corporate resources
  • Users in restrictive regions
  • Privacy-conscious users
  • Travelers using public WiFi

Progressive Response Strategy

function getResponseLevel(ipData, userHistory) {
// Trusted users get full access regardless of VPN
if (userHistory.isEstablished && userHistory.noFraud) {
return "FULL_ACCESS";
}
// New users with VPN
if (ipData.is_vpn || ipData.is_proxy) {
if (userHistory.hasPaymentMethod) {
return "FULL_ACCESS"; // Verified payment
}
return "LIMITED_ACCESS"; // Require verification
}
// Tor = always suspicious
if (ipData.is_tor) {
return "BLOCK_OR_CAPTCHA";
}
return "FULL_ACCESS";
}

API Response Structure

IPBot returns proxy detection data:

{
"ip": "8.8.8.8",
"is_proxy": false,
"is_vpn": false,
"is_tor": false,
"is_hosting": true,
"proxy_type": null,
"risk_score": 0,
"risk_reasons": []
}

Advanced Detection Strategies

1. Velocity Checking

Detect rapid requests from different IPs:

const ipTracker = new Map();
function checkVelocity(userId, ip) {
const key = userId;
const now = Date.now();
if (!ipTracker.has(key)) {
ipTracker.set(key, [{ ip, time: now }]);
return false;
}
const history = ipTracker.get(key);
const recentIPs = history.filter((h) => now - h.time < 60000); // 1 minute
if (recentIPs.length > 5) {
const uniqueIPs = new Set(recentIPs.map((h) => h.ip));
if (uniqueIPs.size > 3) {
return true; // Suspicious: many different IPs
}
}
history.push({ ip, time: now });
ipTracker.set(key, recentIPs);
return false;
}

2. ASN Analysis

Check if IP belongs to known VPN providers:

const knownVPNProviders = [
"M247 Ltd",
"DataCamp Limited",
"Cloudflare, Inc.",
"DigitalOcean, LLC",
];
async function checkASN(ip) {
const data = await fetch(`https://api.ipbot.com/${ip}`).then((r) => r.json());
const isKnownVPN = knownVPNProviders.some((provider) =>
data.organization?.includes(provider),
);
return {
asn: data.asn,
organization: data.organization,
isKnownVPN,
};
}

3. Cross-Referencing Multiple Sources

async function comprehensiveCheck(ip) {
const [ipbot, secondary] = await Promise.all([
fetch(`https://api.ipbot.com/${ip}`).then((r) => r.json()),
fetch(`https://secondary-api.com/${ip}`).then((r) => r.json()),
]);
return {
consensus: {
isProxy: ipbot.is_proxy || secondary.is_proxy,
isVPN: ipbot.is_vpn || secondary.is_vpn,
confidence: ipbot.is_proxy && secondary.is_proxy ? "high" : "medium",
},
ipbot,
secondary,
};
}

Best Practices

1. Don’t Block Blindly

  • Legitimate users use VPNs for privacy
  • Implement CAPTCHA instead of hard blocks
  • Allow known users to bypass VPN checks

2. Log and Monitor

function logSuspiciousActivity(ip, reason) {
console.log({
timestamp: new Date().toISOString(),
ip,
reason,
userAgent: navigator.userAgent,
});
// Send to monitoring system
}

3. Rate Limit Separately

// Stricter limits for proxies
const rateLimits = {
standard: 100, // requests per hour
vpn: 30,
proxy: 20,
tor: 5,
};
function getRateLimit(ipData) {
if (ipInfo.is_tor) return rateLimits.tor;
if (ipInfo.is_proxy) return rateLimits.proxy;
if (ipInfo.is_vpn) return rateLimits.vpn;
return rateLimits.standard;
}

4. User Communication

Be transparent about why you’re blocking:

<div id="blocked-message">
<h2>Access Restricted</h2>
<p>
We detected that you're connecting via a VPN or proxy service. For security
reasons, please disable it and try again.
</p>
<p>If you believe this is an error, please contact support.</p>
</div>

Testing Your Detection

// Test with known IPs
const testIPs = {
googleDNS: "8.8.8.8", // Not a proxy
knownTor: "185.220.101.1", // Tor exit node
knownVPN: "104.28.1.1", // Cloudflare (may flag)
};
async function testDetection() {
for (const [name, ip] of Object.entries(testIPs)) {
const result = await checkProxyStatus(ip);
console.log(`${name} (${ip}):`, result);
}
}

Common Questions

Are all VPN users malicious?

No. Many legitimate users use VPNs for:

  • Workplace security
  • Privacy protection
  • Accessing content while traveling
  • Circumventing censorship

Consider implementing graduated responses rather than blanket blocks.

Can users bypass proxy detection?

Yes, determined users can:

  • Use private VPN services not in databases
  • Rotate through multiple IPs
  • Use residential proxy services

Combine IP detection with behavioral analysis for better results.

Should I block all Tor users?

Blocking all Tor users prevents legitimate privacy-conscious users from accessing your service. Consider:

  • CAPTCHA challenges
  • Rate limiting
  • Requiring account verification
  • Allowing read-only access