A complete step-by-step guide to detecting and blocking VPN, Proxy, Tor, and Private Relay traffic using a Cloudflare Worker and vpnapi.io.
Blocking VPNs and proxies on Cloudflare is one of the most effective ways to prevent fraud, ban evasion, fake signups, and geo-restriction bypasses on your website. This guide explains how to deploy a Cloudflare Worker that checks every incoming IP address against the vpnapi.io real-time detection API and blocks any connection using a VPN, proxy, Tor exit node, or Apple iCloud Private Relay.
| Type | Description | Examples | API Flag |
|---|---|---|---|
| VPN | Routes traffic through a private server to mask real IP | NordVPN, ExpressVPN, split tunneling | data.security.vpn |
| Proxy | Intermediary server forwarding requests on behalf of a client | HTTP/HTTPS/SOCKS proxies, datacenter proxies | data.security.proxy |
| Tor | Anonymity network routing traffic through multiple encrypted relays | Tor Browser, onion routing exit nodes | data.security.tor |
| Private Relay | Apple iCloud Private Relay and similar ISP relay services | iCloud+ subscribers on Safari/iOS | data.security.relay |
Websites block VPN and proxy traffic for a number of legitimate reasons. Cloudflare sits in front of your infrastructure as a reverse proxy, making it the ideal place to enforce this at the network edge — before requests ever reach your servers.
Unlike Cloudflare's built-in firewall rules — which require a paid plan and are limited to known threat IP lists — this Worker-based approach uses vpnapi.io's live detection database to catch VPN traffic in real time, including residential VPNs and lesser-known proxy services.
Navigate to the Cloudflare login page and sign in:
https://dash.cloudflare.com/login
From the Cloudflare Dashboard, go to:
Build → Compute & AI → Workers & Pages → Create application
Choose "Start with Hello World" as the template.
Give your worker a descriptive name such as vpn-block or vpn-proxy-detection, then click Deploy.
.workers.dev URL.
In your Worker dashboard, click Edit code to open the inline code editor showing the default worker.js file.
Clear worker.js and replace it with the complete script below. This worker checks every visitor's IP against vpnapi.io and blocks VPN, proxy, Tor, and relay connections:
export default {
async fetch(request, env, ctx) {
const ip = request.headers.get('cf-connecting-ip');
// For local testing only: const ip = '1.1.1.1';
try {
const url = `${env.VPN_API_URL}/${ip}?key=${env.VPN_API_KEY}`;
const res = await fetch(url);
if (!res.ok) {
throw new Error('Failed to reach vpnapi.io');
}
const data = await res.json();
// Silent passthrough if API returns an error (e.g. invalid key or quota exceeded)
if (data && data.hasOwnProperty('message')) {
return fetch(request);
}
// Block if any of the four threat types are detected
if (
data &&
data.security &&
(
data.security.vpn || // VPN tunnel
data.security.proxy || // HTTP/SOCKS proxy
data.security.tor || // Tor exit node
data.security.relay // iCloud Private Relay or similar
)
) {
return new Response(
'<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>Access Denied</title></head><body style="margin:0;background:#f7f8fa;font-family:Inter,Arial,sans-serif;color:#2c3038;"><div style="min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px;"><div style="max-width:560px;width:100%;background:#fff;border:1px solid #e7eaf0;border-radius:18px;box-shadow:0 10px 30px rgba(44,48,56,.08);padding:36px 28px;text-align:center;"><h1 style="margin:0 0 12px;font-size:32px;line-height:1.15;">Access Denied</h1><p style="margin:0 0 10px;font-size:16px;line-height:1.6;color:#5f6773;">VPN, Proxy, Tor, and Private Relay connections are not permitted.</p><p style="margin:0 0 20px;font-size:16px;line-height:1.6;color:#5f6773;">Please disable your VPN or proxy and try again.</p><p style="font-size:12px;color:#9aa0a6;margin:0;">Powered by <a href="https://vpnapi.io/vpn-database" style="color:#9aa0a6;text-decoration:none;">vpnapi.io</a></p></div></div></body></html>',
{ status: 403, headers: { 'Content-Type': 'text/html' } }
);
}
} catch (e) {
if (e instanceof Error) {
return Response.json(e.message, { status: 400 });
}
return Response.json('Internal Server Error', { status: 500 });
}
// Allow clean traffic through
return fetch(request);
},
};
cf-connecting-ip — a header Cloudflare sets automatically, reflecting the true client IP even behind proxies.vpn, proxy, tor, and relay.fetch(request).Click the file tab to open the Save dialog. Enter a commit message like "add VPN/proxy/tor/relay block" and click Save and deploy.
Invalid URL: undefined/<ip>?key=undefined. This is normal — the environment variables haven't been added yet. Fix this in Step 7.
Go to your Worker's Settings tab → Variables and Secrets → click Add for each of the following:
| Type | Variable Name | Value |
|---|---|---|
| Text | VPN_API_URL |
https://vpnapi.io/api |
| Secret | VPN_API_KEY |
Your vpnapi.io API Key |
Go to the Deployments tab and click Deploy version to push the latest worker code with your environment variables applied. Your worker is now live and blocking VPN and proxy traffic.
The worker currently only runs on its .workers.dev test URL. To protect your real domain, go to Triggers → Add Route:
example.com/* (replace with your domain)example.com/checkout/* — useful if you only want to block VPNs on high-risk pages rather than your entire site.
When a VPN or proxy is detected, users see a clean, professional block page. The page is fully self-contained inline HTML with no external dependencies — light mode by default.
VPN, Proxy, Tor, and Private Relay connections are not permitted.
Please disable your VPN or proxy and try again.
Powered by vpnapi.io
To switch to dark mode, replace the inline style values on the body and card div with:
<!-- Dark mode: update these inline style values --> body → background:#0f1117; color:#e5e7eb; card → background:#1a1d27; border:1px solid #2d3148; box-shadow:0 10px 30px rgba(0,0,0,.4); h1 → color:#f9fafb; p → color:#9ca3af;
<img> tag. All styles are inline so no external CSS is required.
Visitors with a clean IP address pass through the worker transparently — your website loads as normal with zero added latency for legitimate users.
Any visitor using a VPN, proxy, Tor exit node, or iCloud Private Relay receives the 403 block page and cannot access your site until they disconnect.
The vpnapi.io free tier includes 1,000 API requests per day. For higher-traffic sites, cache lookup results in Cloudflare KV to avoid exhausting your quota.
Go to Workers & Pages → KV → Create namespace and name it VPN_CACHE.
Under Settings → Variables → KV Namespace Bindings, add a binding: variable name KV, namespace VPN_CACHE.
Insert this block before the fetch(url) call to vpnapi.io:
// Check cache before calling vpnapi.io
const cacheKey = `vpn:${ip}`;
const cached = await env.KV.get(cacheKey);
if (cached !== null) {
if (cached === 'blocked') {
return new Response('Access Denied', { status: 403 });
}
return fetch(request); // cached as clean — pass through
}
// After fetching from vpnapi.io, cache the result for 1 hour:
const isBlocked = data.security.vpn || data.security.proxy
|| data.security.tor || data.security.relay;
await env.KV.put(cacheKey, isBlocked ? 'blocked' : 'clean', {
expirationTtl: 3600
});
Can I block VPNs on Cloudflare without a paid plan?
Yes. Cloudflare Workers have a generous free tier (100,000 requests/day), and vpnapi.io's free plan includes 1,000 API lookups/day. For most small to medium sites, you can block VPNs and proxies on Cloudflare entirely for free.
Does this block VPN traffic at the Cloudflare level before it hits my server?
Yes. Cloudflare Workers execute at the network edge, before requests reach your origin server. Blocked visitors never touch your infrastructure.
Will blocking Private Relay affect regular iPhone users?
iCloud Private Relay is available to iCloud+ subscribers on Safari and iOS. If you want to avoid blocking legitimate iPhone users, remove data.security.relay from the condition and keep only VPN, proxy, and Tor blocking.
How accurate is vpnapi.io's VPN and proxy detection?
vpnapi.io maintains a continuously updated database of VPN, proxy, Tor, and relay IP ranges. Our crawlers actively scan the internet to detect new services, including residential proxies and lesser-known VPN providers that other lists miss.
Can I only block VPNs on specific pages, like checkout or login?
Yes — when assigning the Worker route in Step 9, use a path-specific pattern such as example.com/checkout/* or example.com/login instead of example.com/*.
Does Cloudflare have a built-in way to block VPNs?
Cloudflare offers limited IP threat scoring on paid plans, but it does not provide dedicated VPN or proxy detection natively. The Cloudflare Worker + vpnapi.io approach gives you precise, real-time detection that covers all four threat types: VPN, proxy, Tor, and Private Relay.
VPN_API_URL or VPN_API_KEY are missing. Follow Step 7 and redeploy from the Deployments tab.
Test your IP at vpnapi.io/vpn-detection to confirm it is flagged. Also check your API key has not exceeded its daily quota in the dashboard.
Remove data.security.relay from the detection condition if iCloud Private Relay is causing false positives for iOS/Safari users.
Confirm the route is correctly configured under Triggers, the pattern matches your domain, and the Zone dropdown has the correct domain selected.
This usually means the vpnapi.io API returned an error. Check that your VPN_API_KEY is correct and that you have not exceeded your daily limit. The free tier resets daily at 00:00:00 UTC.
Ready to block VPNs and proxies on your Cloudflare site? Get a free API key and be live in minutes.
Get Free API Key View API Docs