Supabase: CORS errors from browser [2026 fix]
Browser blocks Supabase API calls due to missing CORS headers. Fix: configure allowed origins in project settings or use proper auth headers.
Supabase CORS Errors from Browser – 2am Fix Guide
TL;DR
Cause: Your Supabase project isn't configured to accept requests from your app's domain. Fix: Add your domain to Supabase project settings → API → CORS allowed origins, or initialize the client with correct auth headers.---
Real Console Error Messages
You'll see one or more of these in your browser DevTools:
``` Access to XMLHttpRequest at 'https://xxxxx.supabase.co/rest/v1/users' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't have required access-control-allow-origin header. ```
``` 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 is 'include'. ```
``` Failed to fetch data from Supabase: No 'Access-Control-Allow-Origin' header is present on the requested resource. ```
``` TypeError: Failed to fetch (With Network tab showing 403 or CORS preflight failure) ```
``` [Supabase] Request failed: 401 Unauthorized - CORS preflight check failed ```
---
Broken Code vs. Fixed Code
❌ Broken: Missing CORS Configuration
```javascript // your-app/pages/dashboard.js import { createClient } from '@supabase/supabase-js'
const supabase = createClient( 'https://xxxxx.supabase.co', 'YOUR_ANON_KEY' )
// App is deployed at https://myapp.com // Supabase project has NO CORS origins configured
export async function getUsers() { const { data, error } = await supabase .from('users') .select('*') return { data, error } } ```
Result: Browser blocks the request. Network tab shows preflight 403.
---
✅ Fixed: Method 1 – Configure CORS in Dashboard
Step-by-step:
1. Go to [Supabase Dashboard](https://app.supabase.com)
2. Select your project → Settings → API
3. Under "CORS allowed origins", click "Add origin"
4. Enter your domain:
- Development: http://localhost:3000
- Production: https://myapp.com
- Wildcard (less secure): *
5. Click Save
Your code stays the same – no changes needed. CORS headers are now sent automatically.
---
✅ Fixed: Method 2 – Client-Side Configuration (If Dashboard Access Denied)
```javascript // your-app/pages/dashboard.js import { createClient } from '@supabase/supabase-js'
const supabase = createClient( 'https://xxxxx.supabase.co', 'YOUR_ANON_KEY', { auth: { persistSession: true, autoRefreshToken: true, }, global: { headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, }, } )
export async function getUsers() { const { data, error } = await supabase .from('users') .select('*') return { data, error } } ```
Note: This helps with headers but doesn't bypass CORS policy itself. Dashboard configuration is still required.
---
✅ Fixed: Method 3 – Use Supabase Auth Token Properly
```javascript // your-app/api/users.js (server-side route – safest) import { createClient } from '@supabase/supabase-js'
// Use SERVICE_ROLE_KEY on server only const supabase = createClient( 'https://xxxxx.supabase.co', process.env.SUPABASE_SERVICE_ROLE_KEY // Never expose in browser )
export default async function handler(req, res) { const { data, error } = await supabase .from('users') .select('*') res.status(200).json({ data, error }) } ```
Why this works: Requests from your server to Supabase don't cross browser CORS boundaries. [More on server-side queries](/?guide=supabase-server-queries).
---
Still Broken? Check These Too
1. Hardcoded localhost vs. production domain
If you added http://localhost:3000 to CORS origins but deployed to https://myapp.com, that domain isn't whitelisted. Add both, or use environment variables:
```javascript
const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL
```
2. Credentials mode mismatch
If you see "wildcard '*' when credentials mode is 'include'", Supabase detected credentials: 'include' somewhere. Remove it or use specific origin instead of *:
```javascript
// Remove if present:
fetch(url, { credentials: 'include' }) // ❌
```
3. Row-Level Security (RLS) policies blocking unauthenticated access CORS passes, but the request fails 403. Check RLS in Supabase Dashboard → Tables → RLS policies. [Debug RLS errors](/?guide=supabase-rls-debug).
---
Quick Checklist
http://localhost:3000 ≠ http://127.0.0.1:3000)NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY for browser clientSUPABASE_SERVICE_ROLE_KEY in frontend code---
Official Docs
[Supabase CORS Configuration](https://supabase.com/docs/guides/api/cors) [Supabase Auth & RLS](https://supabase.com/docs/guides/auth/row-level-security)
---
Found a different variation? Drop it in the comments.