Home Blog About
7 min read

Bypass Geo-Blocking with Cloudflare Workers (No VPN Needed)

Access geo-restricted APIs without a VPN. Deploy a Cloudflare Worker reverse proxy in minutes and route requests through edge nodes near your target.

TechnologyCloudflareAIProxyInfrastructure

Some government APIs and public data portals are only accessible from specific countries. If you’re building a project that needs data from a geo-restricted source — say, a government registry that blocks non-domestic IPs — you’ll hit a wall fast. Requests time out, return 403s, or silently drop.

Cloudflare Workers solve this neatly. They execute at Cloudflare’s edge, which means your request originates from a data centre in or near the target country. No VPN, no rented VPS, no SSH tunnels. Just a few lines of JavaScript running on Cloudflare’s free tier.

🌍 Why Geo-Blocking Exists

Government portals and public APIs often restrict access by IP geolocation for several reasons:

  • Compliance — data residency requirements mandate domestic access only
  • Abuse prevention — blocking foreign IPs reduces bot traffic and scraping
  • Infrastructure protection — limiting access to domestic users reduces load
  • Regulatory scope — some data is only legally distributable within a jurisdiction

This isn’t about bypassing security. If the data is public within the target country, you’re just routing your request through an edge node closer to the source.

⚙️ How Cloudflare Workers Help

Cloudflare has data centres in over 300 cities across 100+ countries. When you deploy a Worker, it runs at the edge — meaning requests to your Worker URL are handled by the nearest Cloudflare node. If you’re fetching data from a geo-restricted government portal, the Worker fetches it from a Cloudflare node near the target country, not from your laptop on the other side of the world.

The architecture is simple:

Your App → Cloudflare Worker (edge) → Target API (geo-restricted)

                              Response flows back

🛠️ Setting Up a Cloudflare Worker Proxy

Prerequisites

  • A free Cloudflare account (dash.cloudflare.com)
  • Node.js installed (for the Wrangler CLI)
  • Basic familiarity with JavaScript

Step 1: Install Wrangler

npm install -g wrangler
wrangler login

Step 2: Create the Worker

mkdir cf-proxy && cd cf-proxy
wrangler init --type javascript

Step 3: Write the Proxy

Replace src/index.js (or worker.js) with:

// Geo-proxy worker — forwards requests to a target URL
// Usage: https://your-worker.workers.dev/?url=https://target-api.go.id/endpoint

const ALLOWED_ORIGINS = [
  "https://yourdomain.com",
  "http://localhost:3000",
];

const ALLOWED_DOMAINS = [
  "api.example.go.id",
  "data.example.go.id",
];

export default {
  async fetch(request) {
    // CORS preflight
    if (request.method === "OPTIONS") {
      return new Response(null, {
        headers: corsHeaders(request),
      });
    }

    const url = new URL(request.url);
    const targetUrl = url.searchParams.get("url");

    if (!targetUrl) {
      return new Response(
        JSON.stringify({ error: "Missing ?url= parameter" }),
        { status: 400, headers: { "Content-Type": "application/json" } }
      );
    }

    // Validate target domain
    const targetHost = new URL(targetUrl).hostname;
    if (!ALLOWED_DOMAINS.some((d) => targetHost.endsWith(d))) {
      return new Response(
        JSON.stringify({ error: "Domain not in allowlist" }),
        { status: 403, headers: { "Content-Type": "application/json" } }
      );
    }

    // Forward the request
    const response = await fetch(targetUrl, {
      method: request.method,
      headers: {
        "User-Agent": "Mozilla/5.0 (compatible; DataProxy/1.0)",
        Accept: "application/json, text/html",
      },
    });

    // Return with CORS headers
    const body = await response.text();
    return new Response(body, {
      status: response.status,
      headers: {
        ...Object.fromEntries(response.headers),
        ...corsHeaders(request),
      },
    });
  },
};

function corsHeaders(request) {
  const origin = request.headers.get("Origin") || "";
  const allowed = ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0];
  return {
    "Access-Control-Allow-Origin": allowed,
    "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
    "Access-Control-Allow-Headers": "Content-Type",
    "Access-Control-Max-Age": "86400",
  };
}

Step 4: Deploy

wrangler deploy

You’ll get a URL like https://cf-proxy.your-account.workers.dev. Test it:

curl "https://cf-proxy.your-account.workers.dev/?url=https://api.example.go.id/data"

Step 5: Customise

  • Add domains to ALLOWED_DOMAINS for each geo-restricted API
  • Restrict origins in ALLOWED_ORIGINS to your actual frontend/backend domains
  • Add rate limiting via Cloudflare’s built-in rate limiting rules
  • Add caching with caches.default for responses that don’t change often

🤖 Setting It Up with an AI Chatbot

You don’t need to write this from scratch. AI coding assistants like Claude or ChatGPT can generate, debug, and deploy Cloudflare Workers for you. Here’s how:

Using Claude (claude.ai or Claude Code)

Paste a prompt like this:

“Create a Cloudflare Worker that acts as a reverse proxy. It should accept a ?url= query parameter, validate the target domain against an allowlist, forward the request, and return the response with CORS headers. The allowed domains are: api.example.go.id, data.example.go.id. Restrict CORS to my domain https://myapp.com.”

Claude will generate the full worker.js, wrangler.toml, and deployment commands. You can iterate:

“Add caching for GET requests with a 5-minute TTL.” “Add a rate limit of 10 requests per minute per IP.” “Make it handle POST requests too, forwarding the request body.”

Using ChatGPT

Same approach — describe what you need:

“I need a Cloudflare Worker that proxies requests to geo-restricted government APIs. It should have a domain allowlist, CORS support, and return JSON errors for invalid requests.”

Then deploy what it generates using wrangler deploy.

Using Claude Code (CLI)

If you use Claude Code, you can do the whole thing in one shot:

claude "Create a Cloudflare Worker proxy for geo-restricted APIs. 
Target domains: api.bmkg.go.id, pom.go.id. 
My frontend: https://myapp.com. 
Include wrangler.toml. Deploy it."

Claude Code will create the files, write the code, and run wrangler deploy for you.

🔒 Security Considerations

A proxy like this needs guardrails:

  • Domain allowlist is mandatory — without it, your Worker becomes an open proxy that anyone can abuse
  • CORS restriction — only allow your own domains, never * in production
  • Rate limiting — Cloudflare offers built-in rate limiting rules; configure them
  • No sensitive headers — don’t forward authentication tokens from the original request
  • Monitor usage — Cloudflare’s free tier gives you 100,000 requests/day; check the dashboard
  • Don’t proxy login pages — only proxy public data endpoints, not authentication flows

📊 Cloudflare Workers Free Tier Limits

ResourceFree Tier
Requests100,000 / day
CPU time10ms per request
Worker size1 MB
Cron triggers5
KV reads100,000 / day
KV writes1,000 / day

For most data-fetching use cases, the free tier is more than enough.

🚨 When This Won’t Work

Cloudflare Workers can’t solve every geo-blocking scenario:

  • Cloudflare-to-Cloudflare — if the target site also uses Cloudflare, the request may be flagged or blocked (Cloudflare recognises its own IPs). You’ll get 403s or 1xxx error codes.
  • IP reputation blocking — some sites block known data centre IPs regardless of location
  • JavaScript-rendered content — Workers can only fetch raw HTTP responses, not execute client-side JavaScript. For SPAs, you need a headless browser (Playwright, Puppeteer)
  • Authentication-gated content — if the portal requires login or CAPTCHA, a simple proxy won’t help
  • Legal restrictions — respect the terms of service. Public data is usually fine; scraping private content is not

💭 Final Thoughts

Geo-blocking is often a blunt instrument. Government portals that publish public data shouldn’t need to block international researchers, developers, or citizens abroad — but they do, usually as a side effect of security policies rather than intentional exclusion.

Cloudflare Workers offer a clean, free, low-maintenance workaround. A few lines of JavaScript, a domain allowlist, and you’re querying geo-restricted APIs as if you were in-country. The free tier handles most use cases, the security model is straightforward, and AI assistants can generate the entire setup in minutes.

The key is to use it responsibly: proxy public data, restrict access to your own applications, and respect rate limits.


Related Posts:

Share: