Supabase: CORS errors from browser [2026 fix]
Browser blocks Supabase API calls due to missing CORS headers. Fix: Add your domain to Supabase project settings or configure proper headers.
Supabase CORS Errors from Browser – Production Fix
TL;DR
Cause: Supabase API rejects your browser's origin because it's not whitelisted in your project settings. Fix: Add your domain to Supabase dashboard → Project Settings → API → CORS Allowed Origins, or proxy requests through your own backend.---
Exact Error Messages You'll See
``` 1. Access to XMLHttpRequest at 'https://[project].supabase.co/rest/v1/users' from origin 'https://yourdomain.com' 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. TypeError: Failed to fetch POST https://[project].supabase.co/rest/v1/posts 403 Access-Control-Allow-Origin: missing
3. 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 (include) is 'include'.
4. No 'Access-Control-Allow-Methods' header is present on the requested resource. Requested method: OPTIONS
5. Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://[project].supabase.co/rest/v1/. (Reason: CORS header 'Access-Control-Allow-Origin' missing). ```
---
Broken Code vs. Fixed Code
❌ BROKEN: Direct browser request without whitelisting
```javascript // This fails because yourdomain.com isn't in Supabase CORS settings const { data, error } = await supabase .from('users') .select('*') .eq('id', userId);
if (error) console.error('CORS Error:', error); ```
✅ FIX #1: Add domain to Supabase CORS whitelist (Recommended for SPAs)
Step 1: Go to [Supabase Dashboard](https://app.supabase.com) → Select Project → Settings → API
Step 2: Locate "CORS Allowed Origins" field
Step 3: Add your domains (one per line): ``` https://yourdomain.com https://www.yourdomain.com https://app.yourdomain.com http://localhost:3000 ```
Step 4: Your existing code now works:**
```javascript // Same code, now CORS passes because domain is whitelisted const { data, error } = await supabase .from('users') .select('*') .eq('id', userId);
if (error) console.error('Error:', error); // ✅ Works at 2am without CORS blocking ```
✅ FIX #2: Proxy through your backend (More secure)
Frontend (no direct Supabase calls): ```javascript // Call YOUR backend instead const response = await fetch('/api/users/' + userId); const { data, error } = await response.json(); ```
Backend (Node/Express example): ```javascript const express = require('express'); const { createClient } = require('@supabase/supabase-js');
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY); const app = express();
app.get('/api/users/:id', async (req, res) => { // Server-to-server call – CORS doesn't apply const { data, error } = await supabase .from('users') .select('*') .eq('id', req.params.id) .single();
if (error) return res.status(400).json({ error }); res.json({ data }); }); ```
✅ FIX #3: Use service role key with anonymous key separation
```javascript // In browser: use ANON key (limited permissions) const supabase = createClient( import.meta.env.VITE_SUPABASE_URL, import.meta.env.VITE_SUPABASE_ANON_KEY // Publicly safe );
// In backend: use SERVICE_ROLE_KEY (full permissions) const supabase = createClient( process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_ROLE_KEY // Never in browser ); ```
---
Version-Specific Notes
Supabase JS SDK v2.x: CORS handling identical to v1.x for REST API calls. If you're uncertain whether your SDK version supports a specific CORS config option, check the [official SDK changelog](https://github.com/supabase/supabase-js/releases).
Supabase hosted (supabase.co domain): CORS enforcement has been consistent since 2023. Self-hosted Supabase instances may have different CORS defaults—consult your instance admin.
---
Still Broken? Check These Too
1. Wildcard domains don't work: Supabase doesn't accept * or *.yourdomain.com. You must whitelist exact origins: https://app.yourdomain.com, https://yourdomain.com, etc. separately.
2. Protocol mismatch: http://localhost:3000 ≠ https://localhost:3000. Ensure your CORS entry matches your exact browser URL (check address bar).
3. RLS policies blocking preflight: Even with CORS fixed, Row-Level Security (RLS) policies on your table can cause 403 errors. Verify your table's RLS policies allow the anon key to perform select, insert, or update as needed. See [RLS troubleshooting guide](/?guide=supabase-rls-errors).
4. Stale browser cache: CORS preflight responses are cached. Clear browser cache (Ctrl+Shift+Del) and hard-refresh (Ctrl+F5) after updating CORS settings.
5. Custom headers without preflight: If sending custom headers, browser sends OPTIONS preflight. Supabase must allow OPTIONS method. This is automatic if CORS is configured correctly.
---
Resources
---
Found a different variation? Drop it in the comments – CORS failures can be tricky with redirects, proxies, and subdomains. Your 2am fix might help someone else.