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:3000localhost:3000http://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