Supabase Edge Function returns 401 (verify_jwt)
An Edge Function returns 401 before your code runs? JWT verification is on, and the Authorization header is missing or is not a user JWT.
Reviewed October 16, 2026
What it means in plain English
A 401 from an Edge Function is either the platform's JWT check or your own code. Supabase's status-code page says 401 means JWT verification is enabled and the request had an invalid or missing JWT.
When verify_jwt is on, that check runs before your function. The default is on. If it fails, your code never runs. The body is then one of the platform messages, including Missing authorization header or Invalid Token or Protected Header formatting. A message you wrote yourself means your function did run and returned 401 on purpose.
Likely causes
- A webhook (Stripe, GitHub, a database webhook) called the function with no Supabase
Authorizationheader. Those senders do not have a user JWT. - The project uses the new
sb_publishable/sb_secretkeys. Supabase says the legacy JWT check only understands the old JWT-based keys, so it rejects the new ones. See Invalid JWT after switching to sb_publishable or sb_secret keys. - The browser called the function with
fetchand did not sendAuthorization.supabase.functions.invokeadds that header. A hand-writtenfetchdoes not. - The function code itself returns status 401. The troubleshooting guide says to search the function for
401.
How to fix it, step by step
- Read the response body.
Missing authorization headerorInvalid Token or Protected Header formattingis the platform check. A sentence you wrote is your code. Supabase's 401 guide also has a SQL query againstfunction_edge_logsthat labels the causeyour_code_returned_401,incompatible_keys,invalid_key, ormissing_auth_header. - If your code returned 401, search the function for
status: 401and fix that branch. A third-party API can also be the source if you forward its status. - For a function that only your signed-in app calls, leave
verify_jwton. Call it withsupabase.functions.invoke, or sendAuthorization: Bearerwith a user JWT. Supabase says the client libraries add the header. - For a webhook or any caller that cannot send a user JWT, turn the platform check off for that function only, then verify the caller inside the function (Stripe's signature, for example). Supabase's function config uses:
[functions.stripe-webhook]
verify_jwt = false
Replace stripe-webhook with the function name. Redeploy after you change it.
- You can also turn the check off without editing that file. In the Functions dashboard, open the function's detail tab and toggle off JWT verification. Or redeploy with
supabase functions deploy YOUR_FUNCTION_NAME --no-verify-jwt. - Do not turn the check off and leave the function open if it reads or writes private data. Supabase says that with
verify_jwtoff, your handler is responsible for authenticating the caller. For a signed-in user, their securing guide useswithSupabasefromnpm:@supabase/server. For a webhook, verify the provider's signature in the handler.
How to check it worked
- The webhook delivery is no longer 401. A Stripe event shows a 2xx on the function, and the function logs show your code ran.
- A call with no
Authorizationheader still gets 401 on any function where you leftverify_jwton. - A signed-in call through
supabase.functions.invokereturns the function's real response.
Sources
- https://supabase.com/docs/guides/troubleshooting/edge-function-401-error-response (opens in a new tab)
- https://supabase.com/docs/guides/functions/status-codes (opens in a new tab)
- https://supabase.com/docs/guides/functions/function-configuration (opens in a new tab)
- https://supabase.com/docs/guides/functions/auth (opens in a new tab)
- https://supabase.com/docs/guides/functions/auth-headers (opens in a new tab)
Related fixes
More Supabase Edge Functions fixes
Get the 10-point launch-risk checklist for vibe-coded apps
For apps built with Lovable, Bolt, Cursor or Replit. Built from the real failures on our fix pages, plus What broke this week: one short email with that week's errors and the fix in plain English.
Keep going on your own.
The guides stay free. A deep leak report is $15. Fix packs are $24, or $49 for all three. There is no done-for-you repair.