Is the Supabase anon key safe to expose?
The anon key is meant to be in your frontend. It is safe exactly to the degree your policies are correct, and meaningless as a security boundary on its own.
The short answer
Yes — it is designed to be public, and every Supabase application ships it in the client bundle. What makes it safe or unsafe is not the key but what sits behind it: the anon key identifies a request as anonymous, and your row-level security policies decide what an anonymous request is allowed to see. With policies in place it grants exactly what you permitted. With row-level security disabled it grants everything.
What does it actually grant?
On its own, nothing. It is closer to a username than a password — it tells the API which project you are talking to and which role you are claiming, and that role is anon.
Every question about what it can reach is really a question about your policies. A table with a policy scoped to authenticated returns nothing to it. A table with row-level security disabled returns every row to it. The key is identical in both cases.
This is why "my anon key leaked" is not, by itself, an incident. It was already public. It is in the JavaScript of every page you serve, and anybody who wanted it could have opened developer tools at any point.
What if RLS is off?
Then the anon key is a full read of that table, available to anyone, with no authentication.
curl -s "https://EXAMPLE-PROJECT.supabase.co/rest/v1/bookings?select=*" \
-H "apikey: ${ANON}" -H "Authorization: Bearer ${ANON}"
This is the combination that constitutes an actual finding, and it is worth stating precisely because the two halves get reported separately and neither is alarming alone:
- A public anon key is the intended design and not a problem.
- A table without row-level security is a problem, but only becomes reachable because a public key exists.
Together they are a data exposure. Apart they are two normal facts. Any tool that reports the first half on its own is generating noise; the useful question is always what an anonymous request actually gets back.
Someone posted mine publicly. What now?
Check your policies rather than your key. Run through every table in your public schema, confirm row-level security is enabled, and then make the anonymous request above against each one to see what comes back.
If the answers are empty arrays, nothing has happened. Someone published a value that was already on your homepage.
If any table returns rows it should not, the key was never the issue — that table was readable by every visitor to your site, and had been since it was created.
Should I rotate it?
Usually not, and rotating it will not fix the case above.
Rotation makes sense if you are moving to Supabase's newer publishable key format, or if you want to invalidate old clients for an unrelated reason. It does not make sense as a response to exposure, because exposure is the design. Rotating an anon key to solve a missing-policy problem replaces a public key with a different public key and leaves the table exactly as readable as it was.
There is one credential where the opposite is true. If what you found in your bundle decodes to a role claim of service_role, that is not an anon key, it bypasses every policy you have written, and it should be rotated immediately — before you change any code. The two tokens look nearly identical, and telling them apart takes one command.
What is the publishable key, then?
The replacement for the anon key in Supabase's newer key format, prefixed sb_publishable_ rather than being a JWT. Same role in the architecture: public by design, constrained by your policies, safe in a browser.
The reason to care about the difference is legibility. A JWT is opaque until you decode it, which is exactly why the anon and service-role keys get confused. A prefixed key says what it is in its first characters, and the matching secret format — sb_secret_ — is unmistakable at a glance. That is a meaningful improvement for anyone auditing a bundle by eye.
Whichever format you are on, the check is the same one, and it is not about the key. Ask the database what it hands to a stranger.
RowShield is an independent product with no affiliation to, or endorsement from, Supabase.