Supabase: CORS errors from browser [2026 fix]
Browser blocks Supabase API calls due to missing CORS headers. Fix: configure allowed origins in Supabase project settings or use service role key with proper middleware.
Supabase CORS Errors: 2am Fix Guide
TL;DR
Cause: Supabase API rejects browser requests because your app's origin isn't in the allowed CORS list. Fix: Add your app URL to Supabase project settings under Authentication > URL Configuration, or proxy requests through your own backend.---
Real Console Error Messages
When you hit this bug at 2am, your browser console shows:
``` Access to XMLHttpRequest at 'https://[project].supabase.co/rest/v1/users' 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. ```
``` Failed to load resource: the server responded with a status of 400 (error: "invalid_request") – CORS preflight failed ```
``` supabase.js:1 POST https://[project].supabase.co/auth/v1/signup 403 Forbidden – Origin not allowed ```
``` CORS error: 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' ```
``` No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' ```
---
Broken Code vs. Exact Fix
Problem Setup
Your frontend (localhost:3000):
```javascript // ❌ BROKEN - CORS will fail import { createClient } from '@supabase/supabase-js';
const supabase = createClient( 'https://abc123.supabase.co', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' );
// This call fails in browser const { data, error } = await supabase .from('users') .select('*'); ```
Supabase project settings: Authentication > URL Configuration is empty
---
Solution 1: Add Allowed Origins (Fastest 2am Fix)
Step 1: In Supabase Dashboard:
1. Go to Project Settings > Authentication > URL Configuration
2. Under "Allowed Origins", add:
- http://localhost:3000 (development)
- https://yourdomain.com (production)
- https://www.yourdomain.com (with www)
- https://*.yourdomain.com (subdomains – check if your version supports wildcards)
Step 2: Code stays the same – CORS headers now pass
```javascript // ✅ WORKS - After adding origin to Supabase settings import { createClient } from '@supabase/supabase-js';
const supabase = createClient( 'https://abc123.supabase.co', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' );
const { data, error } = await supabase .from('users') .select('*'); ```
---
Solution 2: Proxy Through Your Backend (Most Reliable)
If you can't modify Supabase settings (locked down org) or need fine-grained control:
Backend (Node/Express):
```javascript // ❌ BROKEN - Direct browser call const { data } = await fetch( 'https://abc123.supabase.co/rest/v1/users?select=*', { headers: { Authorization: 'Bearer YOUR_ANON_KEY' } } ); ```
✅ FIXED - Proxy endpoint:
```javascript // Backend route: GET /api/users app.get('/api/users', async (req, res) => { const { data, error } = await supabase .from('users') .select('*'); if (error) return res.status(400).json(error); res.json(data); });
// Frontend calls YOUR origin (same-origin = no CORS issue) const response = await fetch('/api/users'); const data = await response.json(); ```
---
Solution 3: Service Role Key + Proper Headers (Not for Browser)
⚠️ NEVER expose service role keys in browser code. Use only on backend:
```javascript // ❌ WRONG - Service key in browser const supabase = createClient(url, SERVICE_ROLE_KEY); // Instant hack vector
// ✅ CORRECT - Service key backend-only // Backend only – uses admin privileges safely const supabase = createClient(url, SERVICE_ROLE_KEY); ```
---
Still Broken? Check These Too
1. Wrong origin format: http://localhost:3000 ≠ localhost:3000 ≠ http://localhost:3001. Port must match exactly. Verify in DevTools Network tab (Request URL shows actual origin).
2. Anon key has no permissions: Your NEXT_PUBLIC_SUPABASE_ANON_KEY has RLS policies denying access. Check Supabase > Authentication > Policies tab. Enable select for public role if appropriate.
3. Credentials mode mismatch: If using cookies, ensure createClient has auth: { persistSession: true } AND fetch includes credentials: 'include'. [See RLS guide](/?guide=supabase-rls-errors).
4. Browser stored origin mismatch: Deployed to different subdomain than configured? AWS CloudFront, Vercel preview URLs, or staging subdomains need separate entries. [Check deployment guide](/?guide=supabase-deployment-cors).
---
Version Notes
I'm not certain if wildcard subdomain support (*.domain.com) works in all Supabase versions (it's been requested since 2023). Test in your project first; if it fails, list subdomains individually.
---
Quick Reference
/auth/v1/ – must configure origins/rest/v1/ – respects same origin rules /realtime/v1/ – separate CORS config (some versions)Official Supabase CORS docs: [https://supabase.com/docs/guides/auth/auth-cors](https://supabase.com/docs/guides/auth/auth-cors)
---
Found a different variation? Drop it in the comments.