Detecting VPNs and proxies
Use IPLocate’s privacy signals to identify anonymized traffic:
privacy.is_vpnprivacy.is_proxyprivacy.is_torprivacy.is_icloud_relayprivacy.is_anonymous(true if proxy, Tor, VPN, or iCloud Relay)
Server-side example (Express)
import express from 'express';
import IPLocate from 'node-iplocate';
const app = express();
app.set('trust proxy', true);
const client = new IPLocate(process.env.IPLOCATE_API_KEY);
app.post('/checkout', async (req, res) => {
const ip = req.ip; // recommended with trust proxy
const data = await client.lookup(ip);
if (data.privacy?.is_vpn || data.privacy?.is_proxy || data.privacy?.is_tor) {
return res.status(202).json({ action: 'challenge', reason: 'anonymized' });
}
return res.json({ action: 'allow' });
});
Server-side example (Django)
import os
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from iplocate import IPLocateClient
client = IPLocateClient(api_key=os.getenv("IPLOCATE_API_KEY"))
def get_client_ip(request):
xff = request.META.get("HTTP_X_FORWARDED_FOR")
if xff:
ip = xff.split(",")[0].strip()
else:
ip = request.META.get("REMOTE_ADDR")
return ip
@csrf_exempt
def checkout(request):
ip = get_client_ip(request)
data = client.lookup(ip)
if data.privacy and (data.privacy.is_vpn or data.privacy.is_proxy or data.privacy.is_tor):
return JsonResponse({"action": "challenge", "reason": "anonymized"}, status=202)
return JsonResponse({"action": "allow"})
Best practices
- Prefer friction (for example, step-up auth) over blanket blocks to reduce false positives.
- Combine with
privacy.is_hostingandis_abuserwhere applicable. - Cache results and apply exponential backoff on 429s; see caching responses.