Is the Supabase anon key safe to expose in your frontend?
The anon key is meant to be public. Whether that is safe depends entirely on a setting most people never open.
Every Supabase project hands your frontend a key so the browser can query the database directly. People find it in DevTools, panic, and search for whether it should be there. The short answer is yes. The useful answer is that it is only safe because something else is doing the actual protecting — and that something is worth checking.
What the anon key can reach
- Any table with RLS disabled — completely, including rows belonging to other users.
- Any table whose policy is unconditional, such as USING (true), which matches every row for every caller.
- Any table where a SELECT policy exists but INSERT, UPDATE and DELETE were left uncovered.
- Storage buckets marked public, regardless of what your table policies say.
- Nothing at all, on a table with RLS enabled and no policy — that is the correct default for tables no browser should touch.
Test what yours reaches
Two minutes, from outside your app
- 1Open your live app, press F12, Network tab, reload the page.
- 2Click any request to *.supabase.co and copy the apikey header value and the project URL.
- 3Ask the project which tables that key can even see, using the first command below.
- 4Read a table you know holds user data, using the second. An empty array is the answer you want.
Ask your own project as an anonymous stranger
# what does this key know about? curl -s "https://YOUR-PROJECT.supabase.co/rest/v1/" -H "apikey: YOUR_ANON_KEY" | head -c 2000 # can it actually read rows? curl "https://YOUR-PROJECT.supabase.co/rest/v1/profiles?select=*&limit=3" -H "apikey: YOUR_ANON_KEY" -H "Authorization: Bearer YOUR_ANON_KEY"
An empty array means the table is protected. Rows of real data mean it is readable by anyone who opens DevTools on your site, which is roughly the same as publishing it. A permission-denied message is also a pass — that is the database refusing at the role level.
Where people go wrong
- Assuming 'public key' means 'nothing to configure' — the key is public precisely because RLS is supposed to be doing the work.
- Storing role or plan information in user metadata the client can edit, then writing policies that trust it.
- Leaving a public storage bucket holding user uploads, which no table policy affects.
- Trying to hide the key with obfuscation instead of testing what it can read.
Check what my Supabase anon key can actually reach, and fix what is open. 1. Confirm which key is in my client bundle. Decode any long eyJ token and report its role claim — if it is service_role rather than anon, stop and tell me immediately, because that key ignores every policy. 2. For every table in the public schema, report whether RLS is enabled and list the full USING and WITH CHECK clauses of each policy. 3. Flag any policy that is unconditional (USING true) or that depends on a value the client controls, such as a role in editable user metadata. 4. Check storage buckets too — a public bucket ignores table policies. 5. Fix what is open: enable RLS everywhere, replace unconditional policies with ownership checks written as USING ((select auth.uid()) = user_id), and for tables no browser should touch, enable RLS with no policy so they deny by default. Do NOT tell me to hide or obfuscate the anon key — it is public by design. Finish with a table: table, RLS on/off, and whether an anonymous user can read it.
Common questions
- Is it safe to expose the Supabase anon key?
- Yes, by design — it is meant to ship in your browser bundle, and Supabase's own documentation says so. It is safe only in the sense that a front door key is safe when the locks inside work: the anon key is constrained by Row Level Security, and with RLS off it reads your whole database.
- Can someone do damage with just my anon key?
- Only as much as your policies permit. Against a properly configured project they get nothing they could not get from your own app. Against a project with RLS disabled or a policy written USING (true), they get every row in that table.
- Should I try to hide the anon key?
- No, and you cannot. Anything the browser sends is visible to whoever controls the browser — obfuscating it costs effort and changes nothing. The time is far better spent verifying your policies.
- What about the newer publishable and secret keys?
- Supabase now issues keys prefixed sb_publishable_ and sb_secret_ alongside the older JWTs. The split is the same one: publishable replaces anon and belongs in the browser, secret replaces service_role and must never leave your server.
This is one check out of 40+
Paste your site address and we run the whole list from the outside — leaked keys, open databases, unprotected pages — then hand you one prompt that fixes what we find. Free, about 30 seconds, no signup.
Check my site — free