Map Background

How to Block VPNs and Proxies on Cloudflare

A complete step-by-step guide to detecting and blocking VPN, Proxy, Tor, and Private Relay traffic using a Cloudflare Worker and vpnapi.io.

Overview

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.

What Gets Detected & Blocked
TypeDescriptionExamplesAPI 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

Why Block VPNs and Proxies on Cloudflare?

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.

  • Prevent fraud and chargebacks — fraudsters often use VPNs to spoof their location and bypass payment verification.
  • Stop ban evasion — blocked users can re-register with a new IP via a VPN. Blocking VPNs prevents this.
  • Enforce geo-restrictions — ensure users can only access region-specific content or pricing from their real location.
  • Reduce fake accounts and spam — VPN and proxy IPs are commonly used to create disposable accounts in bulk.
  • Protect against bots and scrapers — automated traffic often routes through proxy networks and datacenter IPs.

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.

Prerequisites

  • A Cloudflare account with a domain configured (free plan works)
  • A vpnapi.io account — sign up free at vpnapi.io/signup
  • Your vpnapi.io API Key from your dashboard
1

Log In to Cloudflare

Navigate to the Cloudflare login page and sign in:

https://dash.cloudflare.com/login
2

Create a New Worker

From the Cloudflare Dashboard, go to:

Build → Compute & AI → Workers & Pages → Create application

Choose "Start with Hello World" as the template.

3

Name & Deploy the Worker

Give your worker a descriptive name such as vpn-block or vpn-proxy-detection, then click Deploy.

💡 Tip After deploying, click Visit to preview the live Hello World response at your temporary .workers.dev URL.
4

Open the Code Editor

In your Worker dashboard, click Edit code to open the inline code editor showing the default worker.js file.

5

Paste the VPN Detection Code

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);
  },
};
What the Code Does
  • Reads the visitor's real IP from cf-connecting-ip — a header Cloudflare sets automatically, reflecting the true client IP even behind proxies.
  • Sends a request to the vpnapi.io API with that IP and your API key stored as a Worker secret.
  • Checks four security flags: vpn, proxy, tor, and relay.
  • Returns a clean, styled 403 block page if any threat is detected.
  • Passes all legitimate traffic through unchanged via fetch(request).
6

Save & Deploy the Code

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.

⚠️ Expected Error After First Save Visiting your worker URL now will show: Invalid URL: undefined/<ip>?key=undefined. This is normal — the environment variables haven't been added yet. Fix this in Step 7.
7

Add Environment Variables

Go to your Worker's Settings tab → Variables and Secrets → click Add for each of the following:

TypeVariable NameValue
Text VPN_API_URL https://vpnapi.io/api
Secret VPN_API_KEY Your vpnapi.io API Key
⚠️ Security Always set your API key as type Secret — not plain Text. Secrets are encrypted at rest and never shown in the dashboard after saving.
8

Final Deployment

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.

9

Assign the Worker to Your Domain

The worker currently only runs on its .workers.dev test URL. To protect your real domain, go to Triggers → Add Route:

  • Route pattern: example.com/* (replace with your domain)
  • Zone: select your domain from the dropdown
💡 Tip You can scope the worker to specific paths, e.g. example.com/checkout/* — useful if you only want to block VPNs on high-risk pages rather than your entire site.

The Block Page

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.

example.com

Access Denied

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;
💡 Customisation The block page is a single inline string — swap colours, update the heading text, or add your own logo as an <img> tag. All styles are inline so no external CSS is required.

Results

✅ Clean Connection (No VPN Detected)

Visitors with a clean IP address pass through the worker transparently — your website loads as normal with zero added latency for legitimate users.

🚫 VPN / Proxy / Tor / Relay Detected → 403 Blocked

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.

Advanced: KV Caching (Optional)

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.

1. Create a KV Namespace

Go to Workers & Pages → KV → Create namespace and name it VPN_CACHE.

2. Bind KV to Your Worker

Under Settings → Variables → KV Namespace Bindings, add a binding: variable name KV, namespace VPN_CACHE.

3. Add Caching to the Worker

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
});

Frequently Asked Questions

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.

Troubleshooting

❌ "undefined/<ip>?key=undefined"

VPN_API_URL or VPN_API_KEY are missing. Follow Step 7 and redeploy from the Deployments tab.

❌ VPN not being detected

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.

❌ Legitimate users getting blocked

Remove data.security.relay from the detection condition if iCloud Private Relay is causing false positives for iOS/Safari users.

❌ Worker not firing on my domain

Confirm the route is correctly configured under Triggers, the pattern matches your domain, and the Zone dropdown has the correct domain selected.

❌ Getting 400 errors

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
Icon