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 → SettingsAPI 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

  • [ ] Added your domain to CORS allowed origins in Supabase dashboard
  • [ ] Domain matches exactly (protocol + port matter: http://localhost:3000http://127.0.0.1:3000)
  • [ ] Using NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY for browser client
  • [ ] Not exposing SUPABASE_SERVICE_ROLE_KEY in frontend code
  • [ ] RLS policies allow your user role to read the table
  • ---

    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.

    🔥 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