Start watching free

Error library · Blocked by CORS

CORS error calling a Supabase Edge Function (“blocked by CORS policy”)

A feature that calls your backend fails. The browser console says “Access to fetch at ‘https://….supabase.co/functions/v1/…’ has been blocked by CORS policy”, often with “Response to preflight request doesn’t pass access control check”.

Why it happens

Your pages and your edge function live at different addresses. So before the real request, the browser sends the function a short “preflight” request asking whether your site may call it. The function has to answer that, and every answer after it, with headers that say yes. When an answer lacks them, the browser throws it away and tells your code only that the request failed.

The three usual causes:

  • The function doesn’t answer the preflight (an OPTIONS request) at all.
  • It sends the headers when it succeeds but not when it crashes, so a real error shows up as CORS.
  • The headers it allows leave out one the Supabase client sends, such as x-client-info or apikey.

Check it in a minute

  1. Open the live app with the browser’s developer tools (right-click, Inspect) on the Console tab, and use the feature.
  2. If the CORS message names your function, switch to the Network tab and find the two requests to it: an OPTIONS one and the real one.
  3. If the OPTIONS request fails, the function ignores preflight. If it passes and the real one fails, open the function’s logs in Supabase: a crash there is the real problem, reported as CORS.

How to fix it

Give this to the AI that builds your app: Lovable, Bolt, Cursor, Claude Code or any other.

Calls from my live app to my Supabase edge function [FUNCTION NAME] are blocked by CORS ("blocked by CORS policy" / "Response to preflight request doesn't pass access control check").

1. Make the function answer OPTIONS requests first, before any other code, with status 200 and the CORS headers.
2. Use one set of CORS headers on every response, errors and crashes included: Access-Control-Allow-Origin, and Access-Control-Allow-Headers with at least authorization, x-client-info, apikey and content-type.
3. Wrap the handler in try/catch, so a crash still answers with those headers, a JSON { "error": "..." } body and status 500, and logs the real error with console.error.
4. If only my site should call it, set Access-Control-Allow-Origin to my live domain [YOUR DOMAIN] instead of *, and tell me whether the preview address needs to be allowed too.

Then tell me whether the logs show an error behind the CORS message. After the fix, the feature must work on the live app, and a real failure must show its own error, not CORS.

What Vigilia sees of this

  • The failed request: browsers hide CORS details from the page, so Vigilia sees a request to your function that never completed (“Failed to fetch”), raised with its address. When several different services fail in the same visit, that is the visitor’s connection, and it is left out.
  • The real reason (Pro): with server errors connected, a crash inside the function arrives with its message, even when the browser reports only CORS.

What it can’t see: The word “CORS” itself. Browsers keep it in their own console and never tell the page, so the incident reads as a request that couldn’t complete. The function’s logs, or the check above, tell the two apart.

Know before your users tell you

Vigilia watches your live app, explains what breaks in plain language, hands your AI the fix, and confirms it held with real visitors.

More from the error library