Is it safe to expose your Supabase anon key?
Shaig Khaligli, security engineer
· 4 min read
Yes. The Supabase anon key, now called the publishable key, is designed to sit in your frontend where anyone can read it. It isn't a secret and you don't need to hide it. What it can reach is decided by your Row Level Security policies, so the real question is whether those policies are correct.
That distinction trips up a lot of people, especially on apps built quickly with AI tools, so it's worth being precise about it.
Key takeaways
- The publishable key (
sb_publishable_...) and the legacy anon key are safe to ship in a browser. Supabase's docs say so directly.- They're only safe because RLS limits what they can do. A table without RLS is readable and writable by anyone with the key.
- The secret key (
sb_secret_...) and legacyservice_rolekey bypass RLS completely and must never reach a browser.- Supabase is deprecating the anon and service_role keys by the end of 2026, in favour of publishable and secret keys.
What Supabase says about the key
Supabase's API key documentation describes the publishable key as "safe to expose online: web page, mobile or desktop app, GitHub actions, CLIs, source code", and adds: "Anyone can read it, so it only reaches what Row Level Security allows" (Supabase docs, API keys).
That second sentence is the whole story. Row Level Security (RLS) is a Postgres feature that filters which rows each user can read or change, and on Supabase it's what stands between a public key and your data. The key is a public identifier for your project. It lets the browser talk to your database through the REST API as the anon role, or as authenticated once a user signs in. It grants nothing on its own.
The same page notes that Supabase is moving to new key names: publishable keys (sb_publishable_...) and secret keys (sb_secret_...), and deprecating the old anon and service_role keys by the end of 2026. If your project still uses the JWT-style anon key starting with eyJ, everything in this post applies to it too.
When an exposed key becomes a real problem
The key becomes dangerous the moment a table in an exposed schema has RLS switched off.
Supabase's docs on tables put it plainly: "Until you enable row level security and write a policy, anyone holding your project's publishable key can read and write every row in it" (Supabase docs, Tables). Tables created through the Dashboard get RLS enabled by default. Tables created in the SQL Editor or by migrations don't, and you have to enable it yourself (Supabase docs, Securing your API).
This isn't theoretical. In March 2025, a researcher scanned 1,645 projects built with Lovable, an AI app builder that uses Supabase, and reported 303 vulnerable endpoints across 170 of them, exposing data such as emails, phone numbers, payment details and API keys (Matt Palmer). It was registered as CVE-2025-48757. Lovable disputes the CVE, arguing that each customer is responsible for protecting their own app's data. Either way, the mechanism was the same one described above: a public key, and tables whose RLS didn't hold.
The key you actually need to protect
The secret key is the opposite of the publishable key. Supabase's docs say it "bypasses every Row Level Security policy you have. Never put one in a browser, a shipped application, or source control." That's because the service_role behind it has Postgres's BYPASSRLS attribute, so policies never apply.
The usual ways it leaks:
- Using the secret key in client code "temporarily" to get past an RLS error.
- Putting it in an environment variable with a public prefix such as
NEXT_PUBLIC_,VITE_orEXPO_PUBLIC_. Those get bundled into the frontend. - Committing a
.envfile.
If you think it's leaked, follow Supabase's order: fix the root cause first, create a new secret key, replace it everywhere, and only then delete the old one. For legacy keys, you deactivate them in the same Dashboard section. We've written a step-by-step guide on checking whether your secret key leaked.
How to check what your public key can see
You can test this in a minute. Take your publishable or anon key and query a table as an anonymous visitor would:
curl 'https://YOUR_PROJECT.supabase.co/rest/v1/profiles?select=*' \
-H "apikey: YOUR_PUBLISHABLE_KEY"
If that returns rows you didn't mean to make public, you have an RLS problem, not a key problem. Rotating the publishable key won't fix it; the next one will be public too.
To check every table at once, our free exposure checker runs in your browser and shows how many rows each table returns to the public key, plus which database functions are callable. Your key goes only to your own Supabase project.
Then go through the Supabase RLS checklist. Reads with the public key are just one of twelve things worth checking. The others, like WITH CHECK on write policies and SECURITY DEFINER functions, are easy to miss because the table policies can look fine.
The short answer
Stop worrying about the publishable key being visible. Worry about RLS. Make sure every table in public has it enabled, that your policies limit rows to the right users, and that the secret key never leaves your server. If you'd like a second pair of eyes, our premium scan is a manual review of all of it, and if we find no high or critical issues, you don't pay.