Supabase service_role key exposed: what to do, in order
A Supabase service_role key (or a newer sb_secret_ key) bypasses Row Level Security. Anyone who holds one can read, change or delete any row in your database. If one ever reached a browser bundle, a public repo or a screenshot, treat it as compromised. Move it to server-side code first, then rotate it, then look for misuse.
How it usually happens in AI-built apps
- The key is put in a public environment variable, such as
VITE_SUPABASE_SERVICE_ROLE_KEY(Vite, Lovable, Bolt) orNEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY(Next.js, v0). Those prefixes exist to ship values to the browser. - An AI agent swaps in the service key to make a permissions error go away.
- A
.envfile is committed to git once, then deleted later. It stays in the history. - A host serves
/.envor/.git/by mistake.
The normal anon or publishable key is different. It is meant to be public, and your Row Level Security policies decide what it can reach. See Supabase RLS check for AI-built apps.
Step 1: Confirm what is exposed
Find your bundle file names in the page source, then search them from a terminal:
curl -s https://your-app.com/assets/index-XXXX.js \
| grep -Eo "sb_secret_[A-Za-z0-9_-]+|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+"
If you find a token starting with eyJ, decode the middle part (it is base64 JSON) and read the "role" field. anon is expected. service_role is the emergency.
Check git history too:
git log -S "sb_secret_" --all --oneline
git log -S "service_role" --all --oneline
git log --all --oneline -- .env .env.local
Step 2: Fix the cause first
Move every use of the secret key into server-side code (Edge Functions, API routes, server actions), stored in a variable without a public prefix. Redeploy the frontend so the key is no longer in the bundle. If you rotate first, the replacement key will leak the same way.
Step 3: Rotate the key
Follow Supabase's own documentation for rotating a leaked key, since the dashboard changes over time. The order that works:
- Create a new secret key in your project's API key settings.
- Replace the old key everywhere: host environment variables, CI secrets and Edge Function secrets.
- Confirm each part of the app works on the new key.
- Retire the old key. A secret key can be deleted, and legacy JWT keys can be deactivated.
- Verify that a request with the old key now fails.
Step 4: Rotate everything nearby
If a .env file was exposed, assume every key in it leaked: Stripe, OpenAI, email providers and so on. Rotate each one at its own provider.
Step 5: Look for misuse
Check your Supabase API logs for requests that do not match normal app traffic during the time the key was exposed. If personal data may have been read, treat it as a possible breach and look up your notification duties where you and your users live. This page is not legal advice.
Step 6: Prevent a repeat
- Add a build check that fails if
sb_secret_or a service_role token appears in the build output. - Turn on GitHub secret scanning and push protection if you use GitHub.
- Use a secret-scanning pre-commit hook such as gitleaks.
- Re-check your RLS policies. A leaked secret key often means the app was relying on it instead of policies.
- Check that source maps are not public, since they make code easier to search.
How DeployVet fits
The free check searches your public JavaScript files for secret-looking keys, including Supabase service_role tokens and sb_secret_ keys, and reports them as critical findings. It cannot see keys that never reach the browser, your git history, or your server logs. The paid Scan can also read a public GitHub repository you point it to.
FAQ
Is the Supabase anon key a leaked key?
No. It is built to be public. Your protection comes from Row Level Security.
I removed the key from my code. Am I safe?
Not yet. It may still be in git history, old deployments or cached files. Rotate the key.
Do I need to rotate if nobody seems to have used it?
Yes. You cannot tell who copied a public file. Rotating is the only way to be sure the old key stops working.
Does DeployVet find an exposed service_role key?
If it is in a public script or file on your live site, yes. The Free check reports it as a critical finding.
Check your site for free
DeployVet checks your live site from the outside, like a stranger would. You get a score out of 100 and the most important finding with a fix.
DeployVet has three plans. The Free check is $0. A one-time Scan is $7. The Fix it pack is $10 once and adds 30 days of "Did I fix it?" re-checks and score history. For paid plans you pay first, then the scan runs.
DeployVet is an outside-in check, not an audit, a penetration test or a certification. It sees only what a stranger can reach from the public internet, and a clean result is not a guarantee. Only check sites you own or have permission to check. See the methodology for what is and is not checked.