Supabase Edge Function CORS error: Response to preflight request doesn't pass access control check

Calling a Supabase Edge Function from the browser fails with a CORS/preflight error? Handle OPTIONS and return corsHeaders on every response.

Reviewed October 14, 2026

What it means in plain English

Before calling your Edge Function, the browser sends a quick "am I allowed?" (OPTIONS) request. If the function doesn't answer it with the right headers, the real request is blocked.

Likely causes

  • The function doesn't handle the OPTIONS method.
  • CORS headers are returned on success but not on error responses.
  • The allowed-headers list is missing headers the Supabase client sends (authorization, apikey, x-client-info, content-type).

How to fix it, step by step

  1. Easiest: wrap the function with withSupabase from npm:@supabase/server@^1, which handles CORS and preflight automatically.
  2. Or handle it manually (supabase-js v2.95.0+):
import { corsHeaders } from 'npm:@supabase/supabase-js@^2/cors'
export default {
  fetch: async (req) => {
    if (req.method === 'OPTIONS') return Response.json({ ok: true }, { headers: corsHeaders })
    try {
      const body = await req.json()
      return Response.json({ ok: true, body }, { headers: corsHeaders })
    } catch (e) {
      return Response.json({ error: String(e) }, { status: 400, headers: corsHeaders })
    }
  },
}
  1. Older supabase-js (before v2.95.0): create _shared/cors.ts with Access-Control-Allow-Origin, Access-Control-Allow-Headers: authorization, x-client-info, apikey, content-type, x-retry-count, traceparent, tracestate, baggage and allowed methods, and import it.
  2. Redeploy the function (supabase functions deploy <name>, or ask Lovable to redeploy it).

How to check it worked

  • Call the function from the live site; the Network tab should show the OPTIONS request returning 200 followed by the real request.
  • Errors from the function should now show their real message instead of CORS.

Sources

Can't fix it? Get help.

If the steps didn't clear the error, send what the screen says. A person can take a look. This is a request for a paid fix, not an instant chat.

Request a fix