fix guide
Supabase Edge Function CORS error on your live site
Harjot · · updated
Your site calls a Supabase Edge Function and the call fails. The browser console shows a CORS error, Failed to fetch, or a message about a preflight request. The function exists, but the browser blocks the call.
Before the real call, the browser sends an OPTIONS request to ask permission. The function has to answer it with CORS headers, and every later reply needs them too. Check these four in order.
1. The function doesn't answer the preflight
The browser sends an OPTIONS request before the real call. Supabase's docs say you must handle these preflight requests to call a function from the browser. Your function answers OPTIONS with a success and the CORS headers, before any other code runs.
Import the shared headers from Supabase's package:
import { corsHeaders } from "npm:@supabase/supabase-js@^2/cors";That import needs supabase-js 2.95.0 or later. For older versions, add the headers yourself, as the docs describe. Then answer OPTIONS at the top of the handler:
if (req.method === "OPTIONS") { return Response.json({ ok: true }, { headers: corsHeaders }); }signs The console mentions a preflight request. In the Network tab, the OPTIONS request to the function doesn't show a success.
2. The allowed headers list is missing entries
A browser call can send extra headers, such as authorization and apikey. The function has to list each one as allowed. Supabase's current list is authorization, x-client-info, apikey, content-type, x-retry-count, traceparent, tracestate and baggage.
Keep the import from step 1. A list typed by hand often leaves out authorization or apikey. After you change the function, redeploy it.
signs The console names a header, such as authorization or apikey, as not allowed.
3. Error replies leave out the headers
The CORS headers must go on every reply, including the error replies your code sends. Supabase's example adds them to the catch block, so the browser can read the error message.
return Response.json({ error: message }, { status: 400, headers: corsHeaders });signs Working calls return results. Failing calls show a CORS error where your error text should be.
4. The login check rejects the call
Supabase checks the user's login token before your code runs. Each function has this check on by default. If the token is missing or invalid, the platform answers with a 401 and your code never runs. So your code never adds the CORS headers.
Call the function through the Supabase client, such as supabase.functions.invoke. The client sends the signed-in user's session with the call. Then check that the user really is signed in on the live site.
Don't switch the check off just to clear the error. Supabase's docs say that removes the platform's check, so your function then has to verify each caller itself.
signs The Network tab shows a 401 on the function call. Visitors who aren't signed in hit the error.
Test it like a new user
- Open the live site in a private window and sign in.
- Trigger the action that calls the function.
- Open the Network tab and check the OPTIONS request returns a success.
- Check the function's reply comes back, with no CORS error in the console.
- Trigger an error on purpose, such as a bad input, and check your error message shows on the page.
Common questions
Why does my live site get a CORS error when it calls a Supabase Edge Function?
Your function doesn't answer the browser's OPTIONS request with CORS headers. The browser sends that request before the real call, and the function must answer it with a success and the headers. Import corsHeaders from Supabase's package and return them on OPTIONS before any other code runs. The import needs supabase-js 2.95.0 or later.
Why does my Edge Function still show a CORS error after I added the headers?
Your error replies probably skip the CORS headers, so the browser hides the error text. The CORS headers must go on every reply, including the ones your catch block sends. Pass headers: corsHeaders in every Response, for failing calls as well as working ones.
Why does my Edge Function call return a 401 and never reach my code?
Supabase rejected a missing or invalid login token before your code ran. That check returns a 401 with no CORS headers. Call the function with supabase.functions.invoke so the session is sent, and confirm the user is signed in on the live site. Don't switch the check off.
Still stuck?
Not sure this is the cause? Paste your app's link into the free scan. It lists what a visitor's browser can see, like leaked keys and exposed files, worst first. The score is free; every fix as a prompt for your builder is $9, one-time.
Want a person to look inside? See the diagnosis