VibeSecurity

Comparison

Supabase anon key vs service role key: the difference

Supabase gives every project a low-privilege key and a high-privilege key. Mixing them up is one of the most damaging mistakes in a generated app. This page explains the difference using Supabase's own documentation.

By the VibeSecurity team4 min read

The two keys side by side

From the Supabase API keys documentation on the date above.
Aspectanon key (publishable)service_role key (secret)
New-style equivalentPublishable key, starting sb_publishable_.Secret key, starting sb_secret_.
Postgres roleanon when nobody is signed in, authenticated once a user signs in through Supabase Auth.service_role.
Row Level SecurityApplies. With RLS enabled and no policies, this key reaches no data.Bypassed. The docs say the role has the BYPASSRLS attribute and skips every policy.
Where it may liveDocumented as safe to expose in a web page, mobile or desktop app, CLIs and source code.Backend components only: servers, APIs with their own authorisation checks, Edge Functions and microservices.
Browser useIntended for it.The docs say a new-style secret key does not work in a browser: Supabase matches on the User-Agent header and returns HTTP 401. Do not rely on this for the legacy JWT key.
If it leaksNot a breach on its own, provided RLS and policies are correct.Treat as full database compromise. Replace the key and review access.
StatusLegacy. Supabase states the anon and service_role keys are being deprecated by the end of 2026.Legacy. Same deprecation applies.

What the anon key leaves to you

The anon key is only as safe as your policies. Supabase's RLS guide says a table in an exposed schema without RLS is readable and writable by any role with a grant on it. A public key plus a table without RLS is an open table.

  • Enable RLS on every table in an exposed schema.
  • Write policies per operation and tie them to auth.uid().
  • Remember that auth.uid() returns null for a logged-out request, so test as a logged-out visitor.
  • Avoid using (true) except for data that is meant to be public.

What the service role key leaves to you

Because this key skips every policy, your own server code becomes the access control. Any function that uses it must work out who the caller is and what they are allowed to do before it touches the database.

  • Store it as a server-side secret, never in a variable that a bundler exposes to the client, such as one starting VITE_ or NEXT_PUBLIC_.
  • Verify the caller's token inside each function, and take the user id from the verified token, not from the request body.
  • Use it only where a policy cannot do the job, such as admin tasks and webhooks.
  • Have a rotation plan. The docs note that deleting a new-style secret key cannot be undone, so deploy the replacement first.

Find out which key your app ships

Build the project and search the output. A legacy key is a JWT, so you can decode its middle section and read the role claim. Run this only against your own project.

Check the built bundle
npm run build
grep -rEl "service_role|sb_secret_" dist .next/static 2>/dev/null

KEY="paste-the-key-your-app-uses"
echo "$KEY" | cut -d. -f2 | base64 -d 2>/dev/null
# "role":"anon" is expected in client code
# "role":"service_role" in client code means rotate now

What to check whichever key you are using

  • The client bundle contains only the publishable or anon key.
  • Every table in an exposed schema has RLS enabled with policies you have read.
  • The secret or service_role key is stored only in server-side secrets.
  • Git history contains neither key type in a committed .env file, or the key has been rotated if it does.
  • Each server function that uses the privileged key rejects requests with no valid user token.
  • You have a plan to move to publishable and secret keys before the legacy keys are deprecated.

Frequently asked questions

Is the Supabase anon key safe to expose?

Supabase documents it as safe to expose in web pages, apps and source code. That safety depends on Row Level Security being enabled with correct policies on every exposed table.

What happens if my service_role key is leaked?

Anyone holding it can read and write all project data, because the role bypasses Row Level Security. Replace the key following Supabase's rotation guidance, update your servers, and review recent database activity.

Can I use the service role key in the frontend?

No. Supabase's docs say secret keys are for backend components only, and never to use a secret key in the browser or expose it to customers.

What is the difference between publishable and anon keys?

They serve the same purpose. Supabase describes anon as the legacy JWT-based equivalent of the publishable key, and service_role as the legacy equivalent of the secret key. Both systems work at the same time while the legacy keys are phased out.

Sources

  1. 1.Supabase docs: API keys
  2. 2.Supabase docs: Row Level Security
  3. 3.Supabase docs: Edge Function secrets
  4. 4.OWASP Secrets Management Cheat Sheet