Supabase: CORS errors from browser [2026 fix]
Browser blocks Supabase requests due to missing CORS headers. Fix: Configure your project's allowed origins in Supabase dashboard or set correct auth headers.
Supabase: CORS Errors from Browser – 2am Production Fix
TL;DR
Cause: Your Supabase project isn't configured to accept requests from your app's domain. Fix: Add your frontend domain to Supabase's CORS allowed origins in Project Settings > API, or ensure you're using the correct API key with proper headers.---
Real Console Error Messages
Here are the exact errors you'll see in DevTools Console (F12 > Console tab):
``` 1. Access to XMLHttpRequest at 'https://[project-ref].supabase.co/rest/v1/your_table' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Missing header 'access-control-allow-origin'.
2. CORS error: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode (include) is 'include'.
3. Failed to fetch from supabase: TypeError: Failed to fetch (Network tab shows 0 bytes, preflight request returns 403)
4. Access to XMLHttpRequest at 'https://[project-ref].supabase.co/auth/v1/signup' from origin 'https://app.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
5. Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://[project-ref].supabase.co/rest/v1/profiles. (Reason: CORS request did not succeed). Status code: (null). ```
---
Broken Code vs. Exact Fix
Issue 1: Wrong/Missing API Key Type
#### ❌ BROKEN: ```javascript import { createClient } from '@supabase/supabase-js';
const supabase = createClient( 'https://[project-ref].supabase.co', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', // Using SERVICE ROLE key in browser! { auth: { persistSession: true } } );
// This triggers CORS because service role keys aren't meant for browser const { data } = await supabase .from('users') .select('*') .catch(err => console.error(err)); ```
#### ✅ FIXED: ```javascript import { createClient } from '@supabase/supabase-js';
const supabase = createClient( 'https://[project-ref].supabase.co', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', // Use ANON PUBLIC key in browser { auth: { persistSession: true }, headers: { 'Access-Control-Allow-Origin': '*' // Supabase handles this server-side } } );
const { data, error } = await supabase .from('users') .select('*');
if (error) console.error('Supabase error:', error.message); ```
Why: Service role keys (sr_...) should only run on your backend. Always use the public ANON key in browser code. [Check your API keys here](https://app.supabase.com/project/_/settings/api).
---
Issue 2: Missing Allowed Origin in Dashboard
#### ❌ BROKEN: ```javascript // Your code is correct, but Supabase dashboard isn't configured // Project Settings > API > CORS Allowed Origins: (empty or wrong domain)
fetch('https://[project-ref].supabase.co/rest/v1/products?select=*', { headers: { 'apikey': 'eyJhbGc...' // public key } }); // Returns 403 with CORS error ```
#### ✅ FIXED: ```javascript // Step 1: Go to Supabase Dashboard // Project Settings > API > CORS Allowed Origins // Add your domains: // - http://localhost:3000 (development) // - http://localhost:5173 (Vite) // - https://myapp.com (production) // - https://*.myapp.com (wildcard for subdomains)
// Step 2: Your code stays the same, now it works: fetch('https://[project-ref].supabase.co/rest/v1/products?select=*', { headers: { 'apikey': 'eyJhbGc...' } }); // Returns 200 with data ```
Important: Leave CORS Allowed Origins empty to disable the check, but this isn't recommended for production security.
---
Issue 3: Credentials Mode Mismatch
#### ❌ BROKEN: ```javascript const supabase = createClient( 'https://[project-ref].supabase.co', 'anon_public_key', { auth: { persistSession: true }, global: { headers: { 'Authorization': 'Bearer token_here' // Triggers credentials check } } } ); // Browser complains: "wildcard '*' not allowed with credentials" ```
#### ✅ FIXED: ```javascript const supabase = createClient( 'https://[project-ref].supabase.co', 'anon_public_key', { auth: { persistSession: true }, // Let Supabase client handle Authorization automatically // Don't manually set Authorization header } );
// Or if using fetch with custom headers: fetch('https://[project-ref].supabase.co/rest/v1/data', { method: 'GET', headers: { 'apikey': 'anon_public_key', // Use apikey, not Authorization 'Content-Type': 'application/json' }, credentials: 'omit' // Don't send credentials if using public key }); ```
---
Still Broken? Check These Too
1. Wrong project URL – Verify you copied the full project URL (includes region). Typos like supaabase.co (two 'a's) won't match allowed origins.
2. Environment variable mismatch – If using .env.local, restart your dev server after editing. Old env values might be cached. Double-check: console.log(import.meta.env.VITE_SUPABASE_URL) matches your dashboard.
3. Auth header conflicts – If you have RLS (Row Level Security) enabled, you need a valid session token. First authenticate: await supabase.auth.signUp() before querying. Unauthenticated users with RLS policies hit CORS failures.
---
Related Guides
---
Official Documentation
[Supabase CORS Configuration Docs](https://supabase.com/docs/guides/api/cors) [Supabase JavaScript Client Docs](https://supabase.com/docs/reference/javascript/introduction)
---
Version Notes
This guide covers:
If you're using v1.x, the client initialization syntax differs. Upgrade with: npm install @supabase/supabase-js@latest
---
Found a different variation? Drop it in the comments below—we'll add it to the guide.