Why Supabase looks “down” only on Jio / JioFiber
If your logs look healthy, Supabase status is green, but users on Jio or JioFiber still can’t log in, you’re probably hitting the same problem we did:
- All Supabase endpoints live under *.supabase.co.
- Some Indian ISPs (including Jio / JioFiber) appear to block or throttle supabase.co domains.
- Your app still works on Wi‑Fi or other carriers, but on Jio it looks like Supabase is down:
- OAuth flows get stuck after choosing a Google account.
- API calls to /auth/v1/* or /rest/v1/* time out.
- Network tab shows long‑pending or failed requests to https://<project>.supabase.co.
In other words: Supabase isn’t down globally; Jio is blocking the supabase.co hostnames.To fix “Supabase down on JioFiber”, we need to:
Stop exposing *.supabase.co to the browser.
Put Supabase behind a domain that Jio doesn’t block (e.g. api.yourapp.com).
Make sure auth and REST still behave exactly the same.The good news: you can do this cleanly with Cloudflare Workers + a custom domain, plus a small adjustment to how you handle Google OAuth.
High‑level fix: put Supabase behind api.yourapp.com
The core of the solution is:
Create a Cloudflare Worker that proxies requests to your Supabase project.Point a domain like https://api.yourapp.com at that Worker.Configure your frontend so all Supabase traffic goes to https://api.yourapp.com, not https://<project>.supabase.co.
From Jio’s point of view, your app is only talking to api.yourapp.com and accounts.google.com. Supabase is hidden behind that layer.
Step 1: Set up a Cloudflare Worker proxy for Supabase
1.1. Create a Worker
In Cloudflare:
Go to Workers & Pages → Create Worker.
Name it something like supabase-proxy.
Attach your domain:Route: https://api.yourapp.com/* → this Worker.
1.2. Configure Worker environment variables
Under the Worker’s Settings → Variables:
- SUPABASE_URL
https://your-project-ref.supabase.co(your original Supabase project URL)
- SUPABASE_ANON_KEY
Your Supabase anon API key (the same one you expose in your frontend).These are used only server‑side in the Worker.
1.3. Worker code: proxy + CORS + header forwarding
Here’s a production‑ready Worker that:
- Proxies requests from api.yourapp.com to SUPABASE_URL.
- Injects the anon key.
- Preserves the user JWT.
- Fixes CORS for browser usage.
- Forwards important Supabase/PostgREST headers so .single(), pagination, etc. work.
Click here to download workers.js file.
Now any call to https://api.yourapp.com/auth/v1/*, /rest/v1/*, /storage/v1/*, etc. behaves like a direct call to Supabase, but with CORS and ISP blocking under control.
Step 2: Point your frontend at api.yourapp.com instead of supabase.co
In your SPA or SSR app, configure Supabase like this:
// Example: src/lib/supabaseClient.ts
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
// your existing options...
});Then in your env (local and production):
VITE_SUPABASE_URL=https://api.yourapp.com
VITE_SUPABASE_ANON_KEY=your-anon-keyEvery supabase.auth.* and supabase.from(...).select(...) call now goes to:
https://api.yourapp.com/... → Worker → https://<project>.supabase.co/...
From Jio’s point of view, no more traffic to supabase.co from the browser.
Step 3: Fix Google auth when Jio blocks Supabase’s redirect
Even after proxying, there’s one more “Supabase down on JioFiber” trap:
- supabase.auth.signInWithOAuth({ provider: 'google' }) uses Supabase’s own redirect flow:
- Browser → api.yourapp.com/auth/v1/authorize (proxy → Supabase).
- Supabase → Google (account picker).
- Google → https://<project>.supabase.co/auth/v1/callback.
That final callback is back to the blocked supabase.co domain, so on Jio the login still fails. To fix this, you must:
Stop using signInWithOAuth on web.
Use Google’s JS / implicit flow to get an ID token directly on your domain.
Hand that ID token to Supabase via signInWithIdToken over the proxy.
3.1. Start Google OAuth on your domain
Use a helper like:
/**
* Supabase Production Proxy via Cloudflare Workers
* Uses ANON key + preserves user JWT
*/
const CORS_HEADERS = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS, PATCH",
"Access-Control-Allow-Headers":
"Content-Type, Authorization, apikey, x-client-info, x-supabase-api-version, accept-profile, content-profile, range, prefer",
"Access-Control-Max-Age": "86400",
};
export default {
async fetch(request, env) {
// Handle CORS preflight
if (request.method === "OPTIONS") {
return new Response(null, { headers: CORS_HEADERS });
}
try {
const url = new URL(request.url);
const targetUrl = `${env.SUPABASE_URL}${url.pathname}${url.search}`;
// Create clean headers for upstream request
const headers = new Headers();
// Always set anon key
headers.set("apikey", env.SUPABASE_ANON_KEY);
// If frontend sends user JWT, preserve it
const authHeader = request.headers.get("Authorization");
if (authHeader) {
headers.set("Authorization", authHeader);
} else {
headers.set("Authorization", `Bearer ${env.SUPABASE_ANON_KEY}`);
}
// Forward content-type if present
const contentType = request.headers.get("Content-Type");
if (contentType) {
headers.set("Content-Type", contentType);
}
// Forward key Supabase/PostgREST headers so behavior matches direct calls
const forwardHeader = (name) => {
const value = request.headers.get(name);
if (value) {
headers.set(name, value);
}
};
forwardHeader("Accept"); // e.g. application/vnd.pgrst.object+json
forwardHeader("Accept-Profile"); // public
forwardHeader("Content-Profile"); // public
forwardHeader("Prefer"); // return=representation, etc.
forwardHeader("Range"); // pagination
const upstream = await fetch(targetUrl, {
method: request.method,
headers,
body:
request.method === "GET" || request.method === "HEAD"
? null
: await request.arrayBuffer(),
redirect: "manual",
});
const newHeaders = new Headers(upstream.headers);
// Add CORS headers
Object.entries(CORS_HEADERS).forEach(([key, value]) => {
newHeaders.set(key, value);
});
return new Response(upstream.body, {
status: upstream.status,
statusText: upstream.statusText,
headers: newHeaders,
});
} catch (error) {
return new Response(
JSON.stringify({
error: "Proxy Error",
message: error.message,
}),
{
status: 500,
headers: {
"Content-Type": "application/json",
...CORS_HEADERS,
},
}
);
}
},
};Now any call to https://api.yourapp.com/auth/v1/*, /rest/v1/*, /storage/v1/*, etc. behaves like a direct call to Supabase, but with CORS and ISP blocking under control.
Step 2: Point your frontend at api.yourapp.com instead of supabase.co
In your SPA or SSR app, configure Supabase like this:
// Example: src/lib/supabaseClient.ts
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
// your existing options...
});Then in your env (local and production):
VITE_SUPABASE_URL=https://api.yourapp.com
VITE_SUPABASE_ANON_KEY=your-anon-keyEvery supabase.auth.* and supabase.from(...).select(...) call now goes to:
- https://api.yourapp.com/... → Worker → https://<project>.supabase.co/...
From Jio’s point of view, no more traffic to supabase.co from the browser.
Step 3: Fix Google auth when Jio blocks Supabase’s redirect
Even after proxying, there’s one more “Supabase down on JioFiber” trap:
- supabase.auth.signInWithOAuth({ provider: 'google' }) uses Supabase’s own redirect flow:
- Browser → api.yourapp.com/auth/v1/authorize (proxy → Supabase).
- Supabase → Google (account picker).
- Google → https://<project>.supabase.co/auth/v1/callback.
That final callback is back to the blocked supabase.co domain, so on Jio the login still fails.To fix this, you must:
- Stop using signInWithOAuth on web.
- Use Google’s JS / implicit flow to get an ID token directly on your domain.
- Hand that ID token to Supabase via signInWithIdToken over the proxy.
3.1. Start Google OAuth on your domain
Use a helper like:
// src/utils/googleWebAuth.ts
const GOOGLE_AUTH_ENDPOINT = 'https://accounts.google.com/o/oauth2/v2/auth';
export function startGoogleWebOAuth() {
if (typeof window === 'undefined') {
return;
}
const clientId = import.meta.env.VITE_GOOGLE_WEB_CLIENT_ID;
if (!clientId) {
console.error('[GoogleWebAuth] VITE_GOOGLE_WEB_CLIENT_ID is not set');
return;
}
const redirectUri = `${window.location.origin}/auth/google`;
const nonce =
(window.crypto && 'randomUUID' in window.crypto
? window.crypto.randomUUID()
: Math.random().toString(36).slice(2)) || '';
const params = new URLSearchParams({
client_id: clientId,
redirect_uri: redirectUri,
response_type: 'id_token',
scope: 'openid email profile',
nonce,
prompt: 'select_account',
});
const authUrl = `${GOOGLE_AUTH_ENDPOINT}?${params.toString()}`;
window.location.href = authUrl;
}Env:
VITE_GOOGLE_WEB_CLIENT_ID=your-google-web-client-id.apps.googleusercontent.comGoogle Cloud:
- Add https://yourapp.com/auth/google to Authorized redirect URIs.
- Add your domains to Authorized JavaScript origins.
3.2. Handle the /auth/google callback and sign in to Supabase
On /auth/google (a React route or similar):
- Read id_token from the URL hash.
- Call supabase.auth.signInWithIdToken.
Example:
// src/pages/AuthGoogleCallback.tsx (conceptual)
const hash = window.location.hash || '';
const hashParams = new URLSearchParams(hash.startsWith('#') ? hash.slice(1) : hash);
const idToken = hashParams.get('id_token');
if (!idToken) throw new Error('No ID token found in URL');
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'google',
token: idToken,
});
if (error) {
// handle error
}
// success: redirect to profile, found-items-map, etc.This call goes to:
- https://api.yourapp.com/auth/v1/token?grant_type=id_token → Worker → Supabase.
No browser redirect to supabase.co is involved.
3.3. Update Supabase’s Google provider
In Supabase dashboard → Auth → Providers → Google:
- Make sure the Client ID matches VITE_GOOGLE_WEB_CLIENT_ID.
- Skip nonce checks:
- Turn this on if you’re not passing the exact same nonce to Supabase.
- It relaxes nonce enforcement while still verifying the ID token signature and audience.
- Keep the built‑in Callback URL:
- https://<project>.supabase.co/auth/v1/callback
(This is used only for the standard OAuth flow, which you’re no longer using on web.)Now even on Jio / JioFiber, your web auth flow is:
- Browser ↔ accounts.google.com.
- Browser ↔ https://yourapp.com/auth/google.
- Browser ↔ https://api.yourapp.com/auth/v1/token (proxy to Supabase).
supabase.co never appears in the browser, so ISP blocking on that domain no longer matters.
Step 4: Verify that “Supabase down on Jio” is actually fixed
Once everything is wired:
- On a Jio / JioFiber connection:
- Open your app.
- Try “Sign in with Google”.
- Confirm in DevTools → Network:
- You see accounts.google.com, yourapp.com, api.yourapp.com.
- You do not see *.supabase.co in the browser.
- After completing login:
- Check REST calls like GET https://api.yourapp.com/rest/v1/profiles?....
- They should return 200 OK with your data, not timeouts.
- Ask affected users: if they previously saw Supabase “down” on Jio, they should now have:
- Working login.
- Working profile / data pages.
- No strange hangs after choosing their Google account.
Final thoughts
If you’re seeing “Supabase down on JioFiber” or only Jio users complaining that your Supabase app doesn’t load:
- Supabase is almost certainly not down.
- Jio is likely blocking *.supabase.co at DNS or IP level.
- Putting Supabase behind a Cloudflare Worker proxy (e.g. api.yourapp.com) and switching to a Google ID‑token + signInWithIdToken auth flow is a robust, production‑grade workaround that keeps your users online while letting Supabase do what it does best behind the scenes.
