Supabase service_role key leaked? Your first-hour plan
A step-by-step plan for a leaked Supabase service_role or secret key: replace it, shut the old one off, check what was touched and stop it happening again.
Leer en español →
If your Supabase service_role key or sb_secret_ key has leaked, assume someone can read, change and delete everything in your database and Storage, because that key bypasses Row Level Security. In the next hour: create a new secret key, move your server code to it, then deactivate or delete the old one. After that, check your logs and data for anything that was touched, and remove the key from wherever it leaked.
This guide walks through that hour in order, then covers how to look for damage and how to make sure the next key stays private.
How do I know the key really leaked?
Treat it as leaked if any of these is true:
- It appears in your front-end bundle. Open your live site, view the JavaScript files in the browser's developer tools (Sources tab) and search for
service_role,sb_secret_or the first characters of your key. - It was ever committed to a public GitHub repository, even if you deleted it later. Git history keeps it.
- It sits in an environment variable with a public prefix, such as
VITE_,NEXT_PUBLIC_orEXPO_PUBLIC_. Those values are bundled into the code visitors download. - It was pasted into a chat, a support ticket, a screenshot or a shared doc.
- You received an alert from GitHub or another scanner about it.
A quick way to tell the keys apart: legacy keys are long JWTs that begin with eyJ. The legacy anon key is meant to be public; the legacy service_role key is not. New keys are short strings starting with sb_publishable_ (public) or sb_secret_ (private). If you are not sure which one you leaked, decode the JWT payload: the role claim says anon or service_role.
What should I do in the first hour?
Work in this order. Replacing the key before shutting the old one off avoids taking your own app down in the middle of an incident.
1. Create a new secret key
In your Supabase dashboard, go to Settings > API Keys and create a new secret key. The Supabase API keys docs recommend separate named secret keys per backend component, so you can rotate one without touching the others. Give this one a clear name, such as webhooks-2026-10.
2. Put the new key everywhere the old one was used
Make a list before you start, because a missed spot will break after the old key is gone. Typical places:
- Edge Function secrets. Edge Functions also receive the new keys automatically through the
SUPABASE_SECRET_KEYSvariable, which holds a JSON object of keys by name. - Your hosting provider's environment variables (Vercel, Netlify, Render, Railway).
- Background jobs, cron scripts and automation tools such as n8n or Zapier.
- Local
.envfiles on any developer's machine.
Redeploy each service after changing the variable. Most hosts only read environment variables at build or start time.
3. Turn the old key off
- If you leaked a new
sb_secret_key: delete it in Settings > API Keys once every service uses the replacement. Supabase notes that deleting a secret key can't be undone, so confirm the replacement works first. - If you leaked the legacy
service_rolekey: deactivate the legacy keys in the same screen. This step is reversible, so you can switch them back on briefly if something you missed stops working. If your front end still uses the legacyanonkey, move it to a publishable key as part of the same change so nothing depends on the legacy pair.
The legacy keys are JWTs signed with your project's legacy JWT secret. If you suspect the JWT secret itself was exposed (not only the key), read the JWT signing keys guide: moving to signing keys and revoking the legacy secret is the fix, and revoking immediately can sign users out.
4. Remove the key from where it leaked
Delete it from the code, the variable with a public prefix, the chat or the document. If it was in a public repository, removing the file is not enough, but you don't need to rewrite history in a panic: once the key is dead, the copy in history is harmless. Clean up later, calmly.
How do I check whether someone used it?
Once the old key no longer works, the clock stops. Now look backwards.
- Check the API logs in your project's logs section for unusual traffic in the window the key was exposed: requests at odd hours, from unfamiliar IP addresses, bulk reads of whole tables, or deletes. Log retention depends on your plan (Supabase's pricing page lists 1 day on Free, 7 on Pro, 28 on Team), so look today rather than next week.
- Look at Auth users. In Authentication > Users, sort by creation date and look for accounts you don't recognise, especially any with admin roles in
app_metadata. - Compare row counts on important tables with what you expect. A sudden drop suggests deletes; a jump in a table like
profilesororderssuggests inserts. - Check Storage for missing or unfamiliar files in your buckets.
- Check Edge Function and database function changes if the key had been used with the Management API or with tools that can change your schema.
If you find damage, restore from the most recent good backup. Supabase backups cover the database, not files in Storage, so deleted files have to come from your own copies. Our guide to Supabase backups explains what each plan keeps.
Do I have to tell my users?
That depends on what data was exposed, where your users live and what the law requires there. Many data-protection laws have notification rules with deadlines. This is not legal advice: if personal data may have been accessed, talk to a lawyer promptly. Our plain-language overview of data protection basics for app founders can help you prepare that conversation.
Why did the key end up in the front end in the first place?
In AI-built apps, the cause is usually one of these:
- A permission error was "fixed" with the wrong key. RLS blocked a query, the AI suggested using the service role key, and the error went away. The real fix was a policy.
- Server code was written as client code. An admin action, a webhook handler or a data import was built as a React component calling Supabase directly.
- A variable got a public prefix. The secret was named
VITE_SUPABASE_SERVICE_KEYso the browser could read it.
How do I stop it happening again?
- Keep secret keys only in server-side places: Edge Function secrets and your host's private environment variables.
- When a query fails with a permission error, fix the policy instead of swapping keys. See our RLS policy examples.
- Move privileged actions into an Edge Function that checks who the caller is before using the secret key.
- Add a search for
service_roleandsb_secret_to your pre-deploy checklist, and turn on secret scanning for your repository if your Git host offers it. - Use one named secret key per service so the next rotation is small.
The same playbook applies to other providers' keys; our guide to an exposed API key in your front end covers OpenAI, Stripe and others.
Quick answers
Can I just rotate the anon key? The anon or publishable key is public by design, so a leak of that key alone is not an incident. A leak of the service_role or secret key is.
Will deactivating the legacy keys log my users out? Deactivating the legacy API keys stops requests that use them. Signing users out is related to the legacy JWT secret, not the API keys. Plan both changes separately.
Is the browser block on secret keys enough protection? No. Supabase rejects secret keys sent from browsers, but anyone can use a leaked key from a script or terminal.
How long do I have? Automated scanners search public repositories and websites for keys all the time. Act as if someone has already found it.
Need help right now?
If you are unsure whether the leak is contained, a Supabase security review checks keys, policies and functions end to end, and the free App Health Check shows what your live site exposes right now. You can also contact us with what happened.


