Securing Supabase Edge Functions: verify_jwt is not enough

Updated

Are my Supabase Edge Functions protected if verify_jwt is on?

Not by itself. verify_jwt only checks that the request carries a valid project JWT, and your public anon key is one. Anyone who opens your website can call the function. Inside the function, check the user with supabase.auth.getUser(), or require a shared secret for cron jobs and a signature for webhooks.

The misunderstanding

Many AI-built apps rely on verify_jwt = true and assume only logged-in users can reach their functions. In our review of public Lovable and Bolt apps, about 3 in 10 had functions that send email, call an AI model or write data with the service role key, without any check of who is calling.

Functions used by your users: check the user

Create a client with the caller's Authorization header and ask Supabase who it is. Return 401 when there is no user, then check the user may do this specific action.

const supabase = createClient(Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_ANON_KEY")!, {
  global: { headers: { Authorization: req.headers.get("Authorization")! } },
});
const { data: { user } } = await supabase.auth.getUser();
if (!user) return new Response("Unauthorized", { status: 401 });

Cron jobs and internal functions: require a secret

Scheduled reports and notification jobs are often public by accident. Send a secret header from the scheduler and compare it with an environment variable.

if (req.headers.get("x-cron-secret") !== Deno.env.get("CRON_SECRET")) {
  return new Response("Forbidden", { status: 403 });
}

Webhooks: verify the signature

Payment and messaging providers sign their webhooks. Verify the signature before trusting the body. See our Stripe webhook guide for the Deno version.

Rate limit anything public

Contact forms, sign-up and password reset flows are public by design. Give them a per-IP and per-email limit and, for forms, a CAPTCHA.

Questions

Should I set verify_jwt = false?

Only for webhooks and other callers that cannot send a Supabase JWT, and then verify a signature or secret in code.

Is the service role key safe inside an Edge Function?

Yes, as an environment variable on the server. The danger is using it to do things for callers you never checked.