Skip to main content

Detecting VPNs and proxies

Use IPLocate’s privacy signals to identify anonymized traffic:

  • privacy.is_vpn
  • privacy.is_proxy
  • privacy.is_tor
  • privacy.is_icloud_relay
  • privacy.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_hosting and is_abuser where applicable.
  • Cache results and apply exponential backoff on 429s; see caching responses.