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
