Skip to content

Security basics

Supabase's new API keys: which one is safe in your app?

Supabase replaced anon and service_role with publishable and secret keys. Which one belongs in your app, and which never does?

Vlad Tkachenko5 min read

In short

  • Supabase now issues sb_publishable_ and sb_secret_ keys. They replace anon and service_role, and do the same two jobs.
  • The publishable key belongs in your app. The secret key never does, and now you can tell them apart by reading the first few characters.
  • Your old keys still work today. Supabase has said all projects will have to move off them in late 2026.

If you opened your Supabase dashboard recently and found keys that start with sb_publishable_ and sb_secret_ sitting where anon and service_role used to be, nothing has gone wrong. Supabase renamed its API keys, and the pair you knew is on its way out.

The change is small in what it does and large in what it prevents. The two keys still do exactly the two jobs they always did. What is new is that you can now tell which is which without opening anything.

What Supabase actually changed

Supabase announced the new keys in July 2025, alongside a change to how it signs authentication tokens. Two things replaced two things:

  • The publishable key, sb_publishable_…, replaces the anon key.
  • Secret keys, sb_secret_…, replace the service_role key.

The privileges are unchanged. A publishable key still identifies your project and carries no permissions of its own; a secret key still bypasses every rule you have written. If you already know which keys are safe in a frontend, none of that intuition is now wrong.

Two practical differences are worth knowing. You can hold several secret keys at once and revoke them one at a time, which means rotating a key no longer means a moment where everything that used it is broken. And the old keys had a property most people never noticed: they were JWTs that expire ten years after you create the project. The new ones do not carry an expiry inside them.

Same two jobs, two different shapes. In the old pair the role is buried in the middle; in the new pair it is the first thing you read.

Which of the two belongs in your app

The publishable key, and only the publishable key.

Your app runs in your visitor's browser, and the request to your database goes out from there. It has to say which project it belongs to, and that identifier travels to every visitor by design. sb_publishable_… is the key built for that: finding it in your app is not a finding, and it never was.

sb_secret_… is the opposite. It reads and writes every row in every table from anywhere, ignoring your policies entirely, which is the whole point of it. It belongs on a server, in an edge function, in a worker. Nowhere a browser can reach. If one has ever been pasted into frontend code, rotate it in the dashboard before you edit anything, because deleting the line does not close the door.

How to tell which kind you have

Read the first few characters. That is the entire check now.

What you seeWhat it isSafe in the browser?
sb_publishable_…current publishable keyBelongs here
sb_secret_…current secret keyNever
eyJ… with anonlegacy publishable keyBelongs here
eyJ… with service_rolelegacy secret keyNever

A current key is one long string in two halves: a random middle and a short checksum on the end. A legacy key is three chunks separated by dots, and the middle chunk is readable text rather than encryption, which is why telling the old pair apart meant decoding it and reading the role field.

If you find a key in your app and cannot tell which it is, our free scan reads your live site from outside and names what it can see. It takes about 20 seconds and needs no account: scan your app.

Do my old anon and service_role keys still work?

Today, yes. Supabase has published a timeline rather than a switch-off:

WhenWhat happens
1 November 2025Projects restored after this date no longer get anon and service_role at all.
Late 2026, exact date not setAll projects will be required to use the new keys.

So there is no emergency this week, and there is a deadline this year. If your app was built before the rename and has never been touched since, it is running on legacy keys right now and will keep running on them for a while yet.

The case for moving early is not the deadline. It is that the day someone pastes the wrong key into a prompt, sb_secret_ announces itself and eyJ… does not.

How to switch without breaking your app

Supabase's own migration guide is the reference. The short version, for an app you did not write by hand:

  1. In the dashboard, open Settings → API Keys and create the new keys. This adds them; it does not remove the old ones, and both pairs work at once.
  2. Find where your app creates its Supabase client and replace the publishable key. In a builder like Lovable or Bolt this is usually one value in your project's settings rather than a line of code.
  3. Replace the secret key anywhere it is used on a server: edge functions, webhooks, scheduled jobs, anything that is not the browser.
  4. Load your app and click through the parts that read and write data. A wrong publishable key fails loudly and immediately, which is the good case.
  5. Only once everything works, disable the legacy keys.

Step 5 is the one to leave for last, and it is worth doing deliberately rather than never: half-migrated projects, where the app still carries a stale anon key beside a live sb_publishable_ one, are common and confusing to debug.

What to do this week

What to do

  • Open Settings → API Keys and see which pairs your project has. That takes a minute and tells you where you stand.
  • If you find sb_secret_… or service_role anywhere in frontend code, rotate it today. That is the only item here that is urgent.
  • Create the new keys and switch your app over when you have half an hour, not because of the deadline but because the prefix makes the next mistake obvious.
  • Check Row Level Security while you are in there. The new key changes nothing about what your policies allow, and switching RLS on is not the same as being protected.

If you want the platform-specific version of what to check, we have a plain language walkthrough for Supabase apps, and the 10-minute security checklist covers this alongside the other things worth switching off in a newly launched app.

FAQ

Is sb_publishable_ the same as the anon key?

It does the same job. It names your project so a request knows where to go, it carries no permissions of its own, and it is meant to sit in your app where anyone can read it. What protects your data in both cases is Row Level Security, not the key.

Do my anon and service_role keys still work?

Yes, today. Supabase has kept them working through the migration and you can hold both pairs at once. It has said that in late 2026 all projects will be required to move to the new keys, without naming the exact day yet.

I have both pairs in my dashboard. Which one should my app use?

Use the publishable key, sb_publishable_. Switching is a one-line change: swap the key your app passes when it creates the Supabase client. Nothing about your tables or your policies changes, because the new key has exactly the privileges the anon key had.

Does the new key format make my app more secure on its own?

No. It makes the dangerous key easier to spot, which is worth real money in mistakes not made, but a publishable key still reads whatever your policies let it read. If Row Level Security is off, the new key opens your database exactly as wide as the old one did.

Written by

Vlad Tkachenko

Founder, Reeve

I spend my time looking at apps built with Lovable, Bolt, v0, Cursor and Replit, and at the short list of mistakes that keep turning up in them.

More about the author

Read next

Not sure where your own app stands?

Run a free scan and get a plain-language grade from A to F in about 20 seconds. No account, no card.

Scan your app free

Automated external check, not a full audit. Absence of findings is not a guarantee of safety.