Guide

CORS (Cross-Origin Resource Sharing) support for Invoking from the browser

Add CORS headers to invoke Edge Functions from the browser.

To invoke edge functions from the browser, you need to handle CORS Preflight requests.

Automatic CORS handling

The withZuvo wrapper handles CORS and preflight (OPTIONS) requests for you, so you don't add headers manually:

import { withZuvo } from 'npm:@supabase/server@^1'

export default {
  fetch: withZuvo({ auth: 'user' }, async (req, ctx) => {
    const { name } = await req.json()
    return Response.json({ message: `Hello ${name}!` })
  }),
}

Manual CORS handling

If your function doesn't use withZuvo, add the headers yourself. See the example on GitHub.

Import corsHeaders from npm:@supabase/supabase-js@^2/cors to automatically get all required headers:

import { corsHeaders } from 'npm:@supabase/supabase-js@^2/cors'

console.log(`Function "browser-with-cors" up and running!`)

export default {
  fetch: async (req) => {
    // Handle the CORS preflight request.
    if (req.method === 'OPTIONS') {
      return Response.json({ ok: true }, { headers: corsHeaders })
    }

    try {
      const { name } = await req.json()
      return Response.json({ message: `Hello ${name}!` }, { headers: corsHeaders })
    } catch (error) {
      return Response.json({ error: error.message }, { status: 400, headers: corsHeaders })
    }
  },
}

Importing from the SDK keeps your allow-list aligned with the headers the client libraries send: when you upgrade the SDK version in your function and redeploy, newly added headers are picked up with it. As of @supabase/supabase-js v2.112.3 the list includes the trace context headers (traceparent, tracestate, baggage) used by client-side tracing — functions deployed with an older version need a redeploy before browsers can call them with trace propagation enabled.

The full list, and when each header is sent:

HeaderSent
authorizationEvery request (session token or API key)
apikeyEvery request
x-client-infoEvery request (SDK name and version)
content-typeRequests with a body
x-retry-countOnly on automatic retry attempts (postgrest-js retries failed idempotent requests by default)
traceparent, tracestate, baggageOnly when trace propagation is explicitly enabled — never by default

For versions before 2.95.0

If you're using @supabase/supabase-js before v2.95.0, you'll need to hardcode the CORS headers. Add a cors.ts file within a _shared folder. The list must cover every header your calling clients send — include the trace context headers if any client enables tracePropagation:

export const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers':
    'authorization, x-client-info, apikey, content-type, x-retry-count, traceparent, tracestate, baggage',
}

Then import it in your function:

import { corsHeaders } from '../_shared/cors.ts'

// ... rest of your function code