Supabase: CORS errors from browser [2026 fix]

Browser blocks Supabase API calls due to missing CORS headers. Fix: Configure allowed origins in Supabase dashboard or use proper auth headers.

Supabase CORS Errors from Browser – Emergency Fix

TL;DR

Cause: Your browser is blocking requests to Supabase because the origin (your domain) isn't registered in Supabase's CORS allowlist. Fix: Add your domain to Supabase Project Settings → API → CORS Allowed Origins, or switch to authenticated requests with proper JWT tokens.

---

Real Console Error Messages

Here are the exact errors you're seeing at 2am:

``` 1. Access to XMLHttpRequest at 'https://xxxxx.supabase.co/rest/v1/users?select=*' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

2. Uncaught (in promise) TypeError: Failed to fetch (Network tab shows OPTIONS request returning 403)

3. Access to XMLHttpRequest at 'https://xxxxx.supabase.co/rest/v1/tables' from origin 'https://myapp.com' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'.

4. Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://xxxxx.supabase.co/rest/v1/auth/v1/token. (Reason: CORS header 'Access-Control-Allow-Origin' missing).

5. TypeError: NetworkError when attempting to fetch resource (usually means preflight OPTIONS failed before actual request even sent) ```

---

Broken Code vs. Exact Fix

❌ BROKEN: Anonymous Requests Without CORS Configuration

```javascript // This fails at 2am because your origin isn't whitelisted const { data, error } = await supabase .from('users') .select('*') .eq('active', true);

if (error) console.log('CORS blocked:', error); ```

Why it breaks: Supabase's REST API requires either: 1. Your origin in CORS allowlist (for anonymous/public requests), OR 2. A valid JWT in Authorization header (for authenticated requests)

✅ FIX #1: Add Domain to CORS Allowlist (Simplest)

In Supabase Dashboard: 1. Go to Project Settings → API 2. Find "CORS Allowed Origins" 3. Add: http://localhost:3000 (dev) and https://yourdomain.com (prod) 4. Click Save 5. Hard refresh browser (Cmd+Shift+R or Ctrl+Shift+R)

```javascript // Same code now works because your origin is whitelisted const { data, error } = await supabase .from('users') .select('*') .eq('active', true); ```

✅ FIX #2: Use Authenticated Requests (More Secure)

Broken: ```javascript const supabase = createClient(url, anonKey); // Anon key + your origin not in CORS list = blocked const { data } = await supabase.from('users').select('*'); ```

Fixed: ```javascript const supabase = createClient(url, anonKey);

// First authenticate user const { data: session } = await supabase.auth.getSession();

if (session?.access_token) { // Authenticated requests include JWT header automatically const { data } = await supabase .from('users') .select('*'); // CORS check is bypassed for authenticated requests } ```

When you use supabase.auth.* methods, the client automatically adds the Authorization header with your JWT. CORS restrictions don't apply the same way to authenticated requests—Supabase validates your token instead.

✅ FIX #3: Explicit Headers (Raw Fetch)

If using raw fetch() instead of Supabase client:

Broken: ```javascript fetch('https://xxxxx.supabase.co/rest/v1/users?select=*') .then(r => r.json()) .catch(e => console.log('CORS error:', e)); ```

Fixed: ```javascript fetch('https://xxxxx.supabase.co/rest/v1/users?select=*', { headers: { 'apikey': 'your-anon-key', 'Authorization': Bearer ${jwtToken}, 'Content-Type': 'application/json' } }) .then(r => r.json()) .catch(e => console.log('Error:', e)); ```

Note on version specificity: Supabase client library (v2+) handles auth headers automatically. If you're on v1.x, behavior may differ slightly—check your package.json.

---

Still Broken? Check These Too

1. Localhost CORS vs. Production Add both http://localhost:3000 AND https://yourdomain.com to CORS allowlist. Supabase treats them as separate origins.

2. Browser Cache and Service Workers Hard refresh (Cmd+Shift+R) doesn't always clear service worker cache. Open DevTools → Application → Service Workers → Unregister, then refresh.

3. Supabase Client Initialization Ensure your client is using correct credentials: ```javascript const supabase = createClient( process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY ); // Verify these are real values, not undefined console.log(process.env.REACT_APP_SUPABASE_URL); ```

---

Related Guides

  • [Authentication failures after CORS fix?](/?guide=supabase-auth-jwt-decode)
  • [Supabase Row-Level Security blocking requests](/?guide=supabase-rls-denials)
  • ---

    Official Documentation

    [Supabase CORS Configuration](https://supabase.com/docs/guides/api/cors)

    ---

    Found a Different Variation?

    Drop it in the comments. CORS errors have subtle variations (preflight timeouts, credential mode mismatches, wildcard origin issues). Your edge case helps others at 2am.

    🔥 0d
    LIVE
    ↓ PlanetScale rage spiking •⚠ Vercel pricing complaints •↑ Railway gaining fast •↑ Supabase happiness rising •↑ Resend loved by devs •↓ PlanetScale rage spiking •⚠ Vercel pricing complaints •↑ Railway gaining fast •↑ Supabase happiness rising
    DEVELOPER PAIN RADAR // Loading...

    Developers complain.
    Opportunities appear.

    We track what developers are struggling with today — and what opportunities that creates.

    —
    guides today
    —
    avg happiness
    🔥 Pain
    📖 Guides
    🔭 Explore
    👤 Mine
    🔥 Pain Radar — rage scores today
    ↗ share
    💡 Opportunity Feed — pain = market gap
    📈 Tool Momentum
    all scores →
    📖 Latest Guide
    all guides →
    📖 All Guides
    📊 Tool Scores
    + Submit
    📰 Hacker News
    ➕ Submit a Tool
    ← back