Tillping
Menu

Fix guides

Stripe webhook to a Supabase edge function returns 401

Supabase edge functions check for a login token by default. Stripe does not send one. So Supabase says no before your code runs.

What you might see

  • Stripe shows failed deliveries with status 401.
  • The reply body says Missing authorization header or Invalid JWT.
  • Your function logs show nothing, because your code never ran.

Why it happens

By default, a Supabase edge function needs a valid JWT in the Authorization header. Your own app sends one. Stripe does not.

Stripe proves who it is with a signature in the Stripe-Signature header instead. So you turn the JWT check off for this one function and check the signature yourself.

How to fix it

  1. Turn off the JWT check for the webhook function. Deploy this one function with the flag below. Or set verify_jwt to false for it in supabase/config.toml.
    supabase functions deploy stripe-webhook --no-verify-jwt
    
    # supabase/config.toml
    [functions.stripe-webhook]
    verify_jwt = false
  2. Check the Stripe signature in your code. Read the raw text of the request. Do not parse it as JSON first. Then check the signature with your endpoint signing secret.
    const body = await req.text();
    const sig = req.headers.get("Stripe-Signature")!;
    const event = await stripe.webhooks.constructEventAsync(
      body, sig, Deno.env.get("STRIPE_WEBHOOK_SECRET")!
    );
  3. Return 400 for a bad signature. If the check fails, return 400. Never return 200 for a request you could not verify.

Check your fix

Run the Tillping scan on your function address. You want a 400 or 401 from your own code for the fake-signature test, and no Invalid JWT message.

Run the free check

Paste your webhook address and see what your endpoint answers. No account. No keys.

Check my webhook