I leaked my Supabase service_role key: what to do
A leaked service_role or sb_secret key bypasses Row Level Security. Remove it from the frontend, create a new secret key, switch your servers to it, then deactivate or delete the leaked key.
Updated 2026-10-16
Treat a leaked service_role key (or a new-style sb_secret_… key) as full read and write access to your database: it bypasses every Row Level Security policy. Fix the leak at its source, create a new secret key under Settings → API Keys, move your servers onto it, then deactivate the legacy keys (or delete the leaked secret key) so the old one stops working.
The order matters: if you switch the old key off before your servers have the new one, your edge functions and webhooks break. If you leave the old one on while you tidy up, it’s still usable by whoever found it. The steps below follow Supabase’s API keys documentation as of October 2026.
Which key did you leak?
| What you see | What it is | Urgent? |
|---|---|---|
sb_secret_… | Secret key (new style) | Yes. Bypasses RLS. |
A long eyJ… string whose middle part decodes to "role":"service_role" | Legacy service_role key | Yes. Bypasses RLS. |
sb_publishable_… or an eyJ… key with "role":"anon" | Public key | No. It’s meant to be public; check your RLS instead. |
Not sure? Paste the first few characters (not the whole key) into our key checker, which runs in your browser and sends nothing.
Step 1: Stop the leak
Supabase’s docs advise fixing the root cause before rotating, otherwise the new key leaks the same way. Usual causes in AI-built apps:
- The key is in frontend code or a
VITE_/NEXT_PUBLIC_variable, so it’s in the JavaScript every visitor downloads. Move whatever needed it into a server-side function. See how to keep keys server-side. - It was committed to Git, pasted into a chat, a screenshot or a support post.
- It was used to “fix” an empty screen because RLS was blocking reads. The real fix is a correct policy: see why RLS returns an empty array.
Step 2: Create a new secret key
- In the Supabase dashboard, open Settings → API Keys.
- Create a new secret key. Creating it doesn’t affect your existing keys: Supabase says new keys sit alongside the legacy
anonandservice_rolekeys until you disable those yourself. - If your frontend still uses the legacy
anonkey, copy the publishable key too. You’ll need it in step 4.
Step 3: Put the new key everywhere the old one was used
Only in server-side places: edge function secrets, your hosting provider’s environment variables (unprefixed), CI secrets, any backend scripts. Redeploy so the new value is picked up. Supabase’s docs say to confirm every component is on the new key before retiring the old one.
Step 4: Switch the old key off
- Leaked a
sb_secret_key: delete it in Settings → API Keys. Deletion is permanent, which is the point. - Leaked the legacy
service_rolekey: deactivate the legacy API keys in the same screen. Supabase notes this is reversible, so you can re-enable briefly if something you missed breaks. Supabase’s docs describe deactivating the legacy keys together, so expect the legacyanonkey to stop working too: move your frontend to the publishable key and redeploy before you do this.
If the JWT secret itself may have leaked (not just a key), rotate the project’s JWT signing key as well. Supabase’s signing keys docs describe moving to a new key without signing users out, and say revoking the old secret immediately can be justified during an active incident.
Step 5: Look for damage
Anyone holding the key could have read, changed or deleted any row and any file, and managed Auth users. Check:
- Your project’s API logs in the dashboard for requests you don’t recognise during the exposure window.
- Authentication → Users for accounts you didn’t expect (especially ones with admin roles in your tables).
- Row counts and recent changes in important tables; restore from a backup if data was altered.
- Storage buckets for new, missing or changed files.
- Anything else stored in the database, such as third-party tokens. If those were readable, rotate them too.
If personal data may have been accessed, you may have legal duties to report it (under UK GDPR, a reportable breach goes to the ICO without undue delay and, where feasible, within 72 hours of becoming aware of it). Take advice if you’re unsure.
Step 6: Make sure it can’t happen again
- Search your built JavaScript for
service_roleandsb_secret_: how to find leaked keys in your bundle. - Keep RLS on for every table, so the public key is all a browser ever needs. Our RLS policy examples cover the common tables.
- Add
.env*to.gitignore, and consider a secret-scanning tool on your repo.
Prompt for your AI builder
Remove every use of the Supabase service_role / sb_secret key from frontend code
and from any VITE_ or NEXT_PUBLIC_ variable. Anything that needs elevated access
must run in a server-side function that reads the key from a server-only secret
and checks the caller is allowed. Use only the publishable key in the browser.
List every file you changed and show me where the secret key is now read.FAQ
Is the anon key leaked too? Do I need to rotate it?
The anon or publishable key is meant to be public, so it being visible isn't a leak. Supabase's docs talk about deactivating the legacy keys as a group, so expect the legacy anon key to stop working too and switch your frontend to the publishable key first. See is the anon key safe to expose.
Supabase says secret keys are rejected from browsers. Doesn't that make a leak harmless?
No. Supabase's docs say a sb_secret_ key sent from a browser gets a 401, but the same key works from a script, curl or a server. They say to delete a leaked secret key immediately.
Will my users be logged out?
Replacing API keys doesn't sign users out. Rotating the JWT signing key doesn't force sign-outs either, according to Supabase's signing keys docs: tokens already issued stay valid until they expire. Revoking the old legacy JWT secret straight away is different, and Supabase says that can be justified during an active incident.
It was only in a private GitHub repo. Do I still need to rotate?
Yes, if anyone besides you can read the repo, or ever could (collaborators, connected apps, a past public moment). Rotating is quick; finding out later that someone used it isn't.
Can I just delete the commit that added it?
Do that as well if you like, but rotate first. Copies of the key can live in clones, forks, CI logs and build caches, and anyone who already saw it still has it.
Check it for free
After rotating, confirm the old key is gone from your live app. Our free scan looks for service_role and sb_secret_ keys in your JavaScript and tests what your public key can read (only after you verify you own the app; nothing is stored). Run a free scan.
Sealed — a short weekly security note for vibe coders
One common security mistake in AI-built apps, how to fix it in your builder, and one useful tool. No spam; unsubscribe any time.