Invalid JWT after switching to sb_publishable or sb_secret keys
The app broke after Supabase's new sb_publishable or sb_secret keys? Legacy keys can stay on until you swap them. Don't send the new keys as a Bearer JWT.
Reviewed October 14, 2026
What it means in plain English
Supabase is replacing the old anon and service_role keys with a publishable key (sb_publishable_…) and a secret key (sb_secret_…). The new keys are not JWTs. If something still treats them like the old keys, the API answers Invalid JWT or 401 and the live app looks broken.
Likely causes
- The new key was sent on
Authorization: Bearer. Supabase's migration guide says the platform then tries to parse it as a JWT and rejects it withInvalid JWT. Send it on theapikeyheader. - An Edge Function still has
verify_jwton. That check only understands the old JWT-based keys. - Edge Function code still reads
SUPABASE_ANON_KEYorSUPABASE_SERVICE_ROLE_KEY. The new values are JSON objects inSUPABASE_PUBLISHABLE_KEYSandSUPABASE_SECRET_KEYS. - A database webhook or
pg_netcall still puts the key onAuthorization. - Legacy keys were turned off before every client, script, and webhook had moved.
How to fix it, step by step
- In the dashboard open Settings → API Keys and the Publishable and secret API keys tab. If you see Create new API keys, creating them is safe: the old
anonandservice_rolekeys keep working beside them. - In browser code, replace the
anonkey with the publishable key. In server code, replaceservice_rolewith the secret key. The publishable key has the same low privileges asanon. The secret key bypasses Row Level Security. Never putsb_secret_…in frontend code or in aVITE_/NEXT_PUBLIC_variable. - Send the new keys on the
apikeyheader only. For an Edge Function, setverify_jwt = falseinsupabase/config.tomland check the caller in your own code, or usewithSupabasefromnpm:@supabase/server@1as the migration guide shows. - In an Edge Function, read the key by name. The default key is named
default:JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS'))['default']. Confirm those secrets exist under Edge Functions → Secrets. - For a dashboard webhook or
pg_netcall, remove theAuthorizationheader that holds the key and sendapikeyinstead. Don't paste the secret into SQL; the guide says to store it in Vault. - Only after every app, mobile build, CI job, and webhook uses the new keys, deactivate the legacy keys on the same Settings → API Keys page. You can turn them back on if you missed a client.
How to check it worked
- A request from the live site returns data or a normal auth response, not
Invalid JWT. - The browser still uses only the publishable key. The secret key appears only in server logs or server env vars.
Sources
Related fixes
Can't fix it? Get help.
If the steps didn't clear the error, send what the screen says. A person can take a look. This is a request for a paid fix, not an instant chat.