If your Supabase service_role key was exposed, treat it as a full database breach and act now, because that key ignores every security rule you set. Unlike the public anon key, the service_role key carries admin access and bypasses Row Level Security entirely, so anyone who has it can read, change, or delete every row in every table. The immediate steps are the same no matter how it leaked: rotate or replace the key, revoke the old one, assume the data was accessed, and move the key server-side so it never ships again.
Short answer
An exposed Supabase service_role key is a full-access breach, not a minor mistake, because the service role "has full access to your project's data" and "uses the BYPASSRLS attribute, skipping any and all Row Level Security policies," per the Supabase API keys documentation. Rotate or replace it immediately in the dashboard, revoke the old key so it stops working, assume every table it could reach was readable and writable, and check your logs. Then find where it shipped and move it to a secure backend.
Why an exposed service_role key is a full breach
The service_role key is an admin credential, and exposing it hands an attacker the same power you have. Supabase is explicit that this role "has full access to your project's data" and skips "any and all Row Level Security policies," according to its API keys documentation. That means the usual safety net, RLS, does nothing here. An anon key that leaks is only as dangerous as your policies allow, but a service_role key that leaks is as dangerous as your entire database.
With this key, an attacker can dump every table, including user records, emails, and anything you thought was protected, and can also insert, update, or delete data. They can create or modify rows in auth tables, tamper with content, or quietly exfiltrate data over time. Because the key bypasses RLS, none of your per-user policies slow them down. That is why a leaked service_role key is handled as a critical incident and not a warning to fix later.
Did you leak user data?
Assume yes until your logs prove otherwise. If the service_role key was ever public, in a client bundle, a public repository, a screenshot, or a shipped mobile app, you must treat all data it could reach as accessed. The honest position for any incident is that exposure equals compromise unless you have evidence to the contrary.
To assess the real impact, check your Supabase logs and any database audit trail for unfamiliar queries, bulk reads, unexpected writes, or access from IP addresses you do not recognize, especially around and after the moment the key became public. Look at auth events for sign-ups or changes you did not make. If you store personal data, this assessment also decides whether you have a reporting obligation under privacy law, so document what you find and when. Do not wait for certainty to start rotating, because containment comes first and forensics second.
Where the key leaked changes your cleanup. If it was committed to a git repository, rotating is necessary but not enough on its own, because the old value stays in the git history and in every clone, and automated bots scan public repositories for keys within minutes of a push. Assume a key pushed to a public repo was found almost immediately, purge it from the history if you can, and rely on the rotation and revocation steps as the real fix rather than a force-push that only hides it.
How to rotate the key right now, step by step
Rotate first and investigate second, because every minute the old key still works is a minute the attacker keeps admin access. The fast path below replaces the compromised key and revokes it so the old value stops working.
| Step | Action |
|---|---|
| 1. Create a new secret key | In the dashboard, open Settings then API Keys and create a new secret key |
| 2. Update your backend | Replace the key in every server, edge function, and CI/CD secret that uses it |
| 3. Test in staging | Confirm your services work with the new key before killing the old one |
| 4. Revoke or delete the old key | Remove the compromised key so it stops working; deleting a secret key is irreversible |
| 5. Rotate the JWT secret if needed | If you use legacy JWT-based keys, rotate and revoke them under Settings then JWT Keys |
| 6. Verify the old key is dead | Send a request with the old key and confirm it is rejected |
Two cautions matter here. First, keep an inventory of everything that uses the key before you revoke it, or you will take down your own backend along with the attacker. Second, if you still use the legacy anon and service_role JWT keys, Supabase is phasing these out in favor of the newer publishable and secret keys; rotating the underlying JWT secret, per the Supabase rotation guide, can invalidate existing tokens and sign users out, so plan for that disruption rather than being surprised by it.
Which keys are safe to expose and which are not
The anon or publishable key is safe to ship, and the service_role or secret key is not, ever. This single distinction is the entire lesson of a service_role leak. The table makes it concrete.
| Key | Safe in the client? | Access level | If it leaks |
|---|---|---|---|
| anon / publishable | Yes | Enforces RLS, low privilege | Low risk on its own when RLS is correct |
| service_role / secret | Never | Bypasses RLS, full read and write | Critical, rotate immediately, treat data as compromised |
If you ever find yourself putting the service_role key in a frontend, a mobile app, or anything a user can download, stop: that work belongs on a server or in an edge function where the key never leaves your control.
The RLS impact: why your policies did not help
Row Level Security did exactly what it was designed to do, which is nothing against an admin key. It is common to assume that strong RLS policies protect the database no matter what, but the service_role key is built specifically to skip them. So a leak of this key is not an RLS failure, it is an architecture failure: a credential that should have stayed on your server ended up somewhere public.
The practical point is that RLS and key hygiene are two separate defenses. RLS protects data accessed with the anon key by ordinary users, while key hygiene keeps the admin key off the client entirely. You need both, and one cannot cover for the other. After you rotate, review your RLS policies as well, because an attacker with admin access may have changed data in ways your policies would normally prevent, and those changes will not undo themselves. If you keep database backups or point-in-time recovery, note the timestamp the key leaked, because a clean restore from just before that point may be the only way to be sure tampered rows are truly gone.
Stop it from shipping again
Most service_role leaks are not database mistakes, they are build mistakes: the key gets baked into a mobile bundle, an Expo or EAS build, an Xcode project, a fastlane config, or a CI/CD secret that gets printed to a public log. Once it ships inside an app a user can download, it is public, and no amount of server-side care undoes that. The durable fix is to keep the key in server environment variables and to scan what you actually ship. Store secrets in your host's environment or a dedicated secrets manager, never in a committed .env file or a client build, and add a pre-commit or CI secret scan so a stray key is caught before it ever leaves your machine.
A scanner like PTKD.com analyzes your built .ipa or .apk and returns a graded report mapped to OWASP MASVS, flagging embedded secrets and exposed keys before you release. It is worth being clear about the boundary: a build scanner cannot rotate your key, read your database logs, or manage your server environment variables, and it will not undo a leak that already happened. What it does is catch the service_role key or other secrets in the app before they reach users, which is the step that would have prevented most of these incidents in the first place.
What to take away
- An exposed service_role key is a full breach because it carries BYPASSRLS and ignores every Row Level Security policy.
- Rotate or replace the key immediately, revoke the old one, and confirm the old value is rejected.
- Assume the data it could reach was accessed; check logs and treat exposure as compromise until proven otherwise.
- RLS never protects against the service_role key, so keep it server-side only, never in a client or mobile build.
- Scan the built app with PTKD.com to catch embedded secrets before they ship.




