Supabase service_role key exposed: what to do in the next 10 minutes
A Supabase service_role JWT or sb_secret key in client code bypasses row-level security. Rotate that key, remove it from the bundle, and redeploy.
A Supabase service_role key bypasses row-level security. So does a secret key that starts with sb_secret_. If either value was in a JavaScript bundle, a public repo, a screenshot, or a chat someone else could open, treat it as copied. Rotating is the fix. Editing the file and hoping nobody saved it is not.
Confirm you are looking at the service key and not the anon key. The two-minute bundle test shows how to decode the JWT locally. A payload role of anon is not this emergency.
Minutes 0–2: name the key
- Three dot-separated chunks starting with
eyJis a legacy JWT. Decode the middle chunk. Proceed only ifroleisservice_role. - A value starting with
sb_secret_is a secret API key. Proceed even if there is no JWT. - Write down which project it belongs to. The JWT payload field
ref, or a nearby*.supabase.cohost, is the project.
Minutes 2–6: rotate the leaked key
Open that project in the Supabase dashboard, then Project Settings and the API or API Keys section.
- Leaked value starts with
sb_secret_: revoke that secret key and create a replacement. This does not, by itself, replace the anon key. - Leaked value is a
service_roleJWT: rotate the project JWT secret. Supabase signs the anon JWT and the service_role JWT with the same secret, so both change, and existing user sessions stop working. Copy the new anon key into the app immediately after.
Update the server environment that is allowed to hold the new secret: a host env var, a Supabase edge function secret, or a backend that is not shipped to the browser. Do not put the new value in a VITE_ or NEXT_PUBLIC_ variable.
Minutes 6–8: get the old key out of the client
Search the repository and the client bundle for service_role and for sb_secret_.
Delete those strings from every client component and from every VITE_ or NEXT_PUBLIC_ variable.
The browser may keep the anon JWT or a publishable key.
The new service secret belongs only in server-side env that is not included in the built JavaScript.
Redeploy the public site so the old bundle is replaced.After deploy, repeat the search on the live URL. A cached CDN copy of the old script can keep serving the key for a while. Purge that cache if the host lets you, then search again.
Minutes 8–10: look for use, then lock tables
In Supabase, open the API logs for the project and look for a burst of requests you do not recognize, especially from the service role. Check Authentication for new users you did not create, and Storage for new public objects. This page cannot see those logs for you.
Then turn on row-level security for private tables. The service role was ignoring those policies; the anon key, which stays in the browser, will not. Use the RLS checker for the SQL and the logged-out GET.
What the free scan actually checks
The scan reads the public homepage and up to six scripts linked from it. It reports a critical finding for an sb_secret_ value, and for a JWT whose role is service_role when the issuer contains “supabase” or the payload includes a project ref. It redacts the value in the report. It does not call your database with that key.
It also GETs a short list of common files, including /.env, /.env.local, and /.env.production, and flags them only when the body looks like that file rather than your homepage HTML. It will not see a key that lives only in a private env var, a git history that is not being served, or a script the homepage does not link.
Questions
Does rotating the JWT secret also change the anon key?
Yes. Supabase signs the legacy anon JWT and the service_role JWT with the same secret. Rotating that secret replaces both and signs users out. Revoking a key that starts with sb_secret_ does not, by itself, replace the anon key.
Will AppPulse use a leaked service_role key to read my database?
No. If the homepage or a linked script contains a Supabase service_role JWT or an sb_secret_ value, the scan reports it and does not send that key to your project.
The key was only in a chat, not in the live site. Do I still rotate it?
Yes, if anyone else could see that chat, a screenshot, or a commit. The free scan only sees files the public site serves. It will not clear a key that never made it into those files.
Other guides
Run it against your live URL
The free scan reads the public site only. No account, no writes, and no use of a service key if one is sitting in the bundle.
Scan your URL