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 endpoints: /auth/v1/ – must configure origins
  • REST API: /rest/v1/ – respects same origin rules
  • Realtime: /realtime/v1/ – separate CORS config (some versions)
  • Dashboard: Project Settings > Authentication > URL Configuration
  • 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.

    🔥 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