Authorization headers
How the Authorization and apikey headers and the verify_jwt platform check work
Every request to an Edge Function passes through two layers of auth. First, a platform-level check (verify_jwt) runs before your code executes. Then, once the request reaches your handler, you decide what to do with the credentials the caller sent. This page is the reference for both layers. For the practical patterns built on top of them, see Securing Edge Functions.
Understanding authorization headers#
Edge Functions care about two request headers. Sending the wrong credential in the wrong header is the most common source of 401 errors.
| Header | Value | Used for |
|---|---|---|
Authorization | Bearer <user-jwt> | A user signed in through Supabase Auth |
apikey | sb_publishable_... or sb_secret_... | Calls from clients or services |
Don't send API keys as bearer tokens
A common mistake is sending a publishable or secret key as a bearer token: Authorization: Bearer sb_publishable_.... The new API keys are not JWTs. The platform check still accepts them, but your handler can't verify them as JWTs. Instead, put API keys in the apikey header.
You can send both headers together. A signed-in user calling your function through supabase-js, for example, sends their session JWT in Authorization and the project's publishable key in apikey.
The verify_jwt platform check#
When verify_jwt is enabled (the default), the platform inspects the Authorization header of every request before your function runs. It expects a valid user JWT. If the header is missing, malformed, or signed with a different key, the platform returns a 401 error, and your code never executes.
The check validates legacy HS256 JWTs and JWTs signed with the new asymmetric signing keys.
Publishable and secret keys are not JWTs, but the check still accepts them in the Authorization header, so callers that send one there reach your handler.
API keys and the verify_jwt check
For migration compatibility, verify_jwt accepts publishable and secret keys on either header, so a key on apikey passes the check too. The check alone doesn't authenticate a caller that sends only an API key. Reserve Authorization for user tokens, and send API keys on apikey. To move off legacy keys entirely, migrate to the @supabase/server SDK as shown in Securing Edge Functions.
Use the verify_jwt flag to match how the function is called:
- Leave
verify_jwton for functions that are only called with a user JWT, such as functions invoked from the client throughsupabase.functions.invoke. The platform rejects unauthenticated requests before they reach your code, and your handler can trust that a valid JWT is present. - Turn
verify_jwtoff for functions that are called without anAuthorizationheader, such as webhooks from external providers, or service-to-service calls that authenticate with an API key. These patterns are covered in Securing Edge Functions.
Set the flag per function in supabase/config.toml:
[functions.stripe-webhook]verify_jwt = falseFor 401 failure modes and how to diagnose them, see Edge Function 401 error response.