Guide

The Supabase service_role key: what it is and how to keep it safe

Every Supabase project ships with two API keys, and one of them is the most dangerous string in your entire stack. The service_role key bypasses every security rule you've set and hands over full control of your database. Here's exactly what it does, where it leaks, and how to make sure yours isn't sitting somewhere it shouldn't.

What the service_role key is

Supabase gives you two keys: the public anon key and the secret service_role key. The difference is everything. The anon key respects Row Level Security (RLS) — the policies that decide who can read and write each row. The service_role key ignores RLS entirely. It's the admin key: any request made with it has full, unrestricted read and write access to every table in your database.

That's by design — it exists so your server can do trusted work (background jobs, admin tasks, migrations) without being blocked by RLS. The problem is only what happens when it escapes the server.

The one rule

The service_role key must only ever live on a server — never in your frontend, never in a mobile app, never in a public repo. Anyone who has it can read, edit or delete your entire database, RLS or not.

Why an exposed service_role key is catastrophic

With a leaked anon key and RLS off, an attacker can read whatever your policies (don't) protect. With a leaked service_role key, none of that matters — RLS is simply skipped. They can dump every table, modify records, or delete your whole database. There's no policy that saves you, because the key's entire purpose is to bypass policies. It is the single worst credential to leak in a Supabase app.

Where the service_role key leaks

It's supposed to stay server-side, so every leak is a case of it ending up somewhere public:

How to check if your service_role key is exposed

A quick manual check:

Tell the two keys apart

Both keys are long eyJ... tokens, so they look alike. Decode the JWT (paste it into any JWT viewer) and read the role claim: anon is safe to be public; service_role is not. We cover this in depth in Exposed Supabase keys: anon vs service_role.

Not sure if your key is exposed?

Paste your app's URL and our free scan checks it from the outside in about 10 seconds — including whether a secret key is reachable in your frontend.

Only scan apps you own or have permission for.

What to do if it's exposed

// DANGER — service_role key in client-exposed env, shipped to the browser const supabase = createClient(url, import.meta.env.VITE_SERVICE_ROLE_KEY) // SAFE — service_role stays server-side; browser uses anon + RLS // server/edge function only: const admin = createClient(url, process.env.SUPABASE_SERVICE_ROLE_KEY)