Is your Supabase app broken without a VPN? Here’s how I fixed it for FREE

Indian ISPs (Jio, Airtel, etc.) are blocking *.supabase.co domains. Your app works fine on VPN but breaks for real users. Auth works, but API calls, images, and storage all fail.

Here’s the fix — a Cloudflare Worker reverse proxy. Takes 5 minutes. Free plan. Zero cost.

Step 1: Create a Cloudflare Worker

Go to https://dash.cloudflare.com → Workers & Pages → Create Worker. Name it something like myapp-proxy.

Step 2: Paste this code

export default {
  async fetch(request, env) {
    const SUPABASE_URL = "https://YOUR_PROJECT_ID.supabase.co";

    const url = new URL(request.url);
    const targetUrl = SUPABASE_URL + url.pathname + url.search;

    const corsHeaders = {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
      "Access-Control-Allow-Headers":
        "authorization, x-client-info, apikey, content-type, accept-profile, content-profile, prefer, range, x-upsert, x-supabase-api-version, x-supabase-client-platform, x-supabase-client-platform-version, x-supabase-client-runtime, x-supabase-client-runtime-version",
    };

    // Handle preflight
    if (request.method === "OPTIONS") {
      return new Response(null, { headers: corsHeaders });
    }

    // Forward the request
    const headers = new Headers(request.headers);
    headers.delete("host");

    const response = await fetch(targetUrl, {
      method: request.method,
      headers,
      body: request.method !== "GET" && request.method !== "HEAD"
        ? request.body
        : undefined,
    });

    // Add CORS headers to response
    const newResponse = new Response(response.body, response);
    Object.entries(corsHeaders).forEach(([k, v]) =>
      newResponse.headers.set(k, v)
    );
    return newResponse;
  },
};

Replace YOUR_PROJECT_ID with your actual Supabase project ref.

Step 3: Deploy & get your Worker URL

Hit “Deploy”. You’ll get a URL like: https://myapp-proxy.yourname.workers.dev

Step 4: Update your Supabase client

import { createClient } from "@supabase/supabase-js";

const supabase = createClient(
"https://myapp-proxy.yourname.workers.dev", // proxy URL
"your-anon-key" // anon key stays the same
);

Step 5: Proxy storage/image URLs too

Your cover_image and audio_url fields in the DB still point to *.supabase.co. Write a helper:

function proxyUrl(url) {
  if (!url) return url;
  return url.replace(
    "https://YOUR_PROJECT_ID.supabase.co",
    "https://myapp-proxy.yourname.workers.dev"
  );
}

Use it wherever you render images or audio from Supabase Storage.

⚠️ The key gotcha: CORS headers

The accept-profile and content-profile headers are sent by the Supabase JS client but aren’t in most CORS examples. If you don’t allow them, you’ll get preflight failures and blank pages. That’s the line most tutorials miss.

💡 Why this works:

  • ISPs block the domain *.supabase.co
  • Your Worker runs on Cloudflare’s edge (not blocked)
  • It forwards requests to Supabase server-side
  • Users never connect to supabase.co directly

Cost: $0. Cloudflare Workers free plan gives you 100K requests/day. More than enough for most apps.

TL;DR: 🔹 Indian ISPs block Supabase 🔹 Cloudflare Worker = free reverse proxy 🔹 Replace Supabase URL in your client 🔹 Don’t forget accept-profile & content-profile in CORS 🔹 Proxy storage URLs for images/audio too

Leave a Reply

Your email address will not be published. Required fields are marked *