The Supabase service role key: what it is and why it must never leave the server
The Supabase service role key grants admin database access that skips RLS. What it is, how AI-built apps leak it, and where the key belongs.
What is the Supabase service role key?
The Supabase service role key is a secret API key that authenticates a trusted backend component with admin-level access to your database. Supabase issues every project two key classes: public keys for browsers and secret keys for servers. The service role key is the secret class, and it carries the most power in the project.
Supabase's API keys documentation defines an API key as the credential that authenticates an application component — a web page, a mobile app — to Supabase services. A browser client gets the anon key and lives under whatever Row Level Security allows. A server gets the service role key and talks to Postgres with admin privileges.
| Key class | Examples | Runs in | Access level |
|---|---|---|---|
| Public | anon, sb_publishable_... | Browser | Whatever RLS allows |
| Secret | service_role, sb_secret_... | Server only | Admin, skips RLS |
Table: The two Supabase key classes and where each one belongs.
Regarding naming, the legacy key reads service_role and the newer generation reads sb_secret_...; both mark the same boundary. Supabase's data security guide states the rule in one line: only use your secret and service role keys on the backend.
Why does the service role key bypass Row Level Security?
Row Level Security policies scope every query to the rows each user deserves. Supabase designed the service_role role for trusted server code, so queries carrying that key skip policy evaluation entirely — the server is expected to enforce access control before the query runs.
Regarding the protection model, Supabase's securing-your-data guide tells you to protect exposed tables with RLS and grant only the privileges each role needs. An anon-key request passes through those policies row by row. A service-role request walks past them. The design fits background jobs, admin tooling, and cron work that must touch every row.
The tradeoff is absolute. Missing policies, RLS disabled, and client-side role checks stay survivable while the service role key stays server-side. The moment the key ships to a browser, every policy in the project turns decorative, because the attacker queries as the one role that ignores policies.
Supabase pairs the two rules: protect exposed tables with RLS, and keep service role keys on the backend. (Source: Supabase docs)
How does the service role key end up in the browser?
AI coding tools frequently hardcode API keys directly in frontend JavaScript, where anyone can extract them using browser dev tools. In AI-built apps the leak arrives as a paste: the model drops the service role key into a client-side environment variable, a fetch header, or a Supabase client constructor, and the bundler ships it to every visitor.
Fencer.dev's vibe-coded app security checklist documents the pattern: AI hardcodes keys in frontend JavaScript, and browser dev tools expose them. Regarding AI-built apps specifically, the model optimizes for a working demo, and a hardcoded key is the shortest path to one.
Three placements account for most leaks I flag in this category:
- A
NEXT_PUBLIC_orVITE_environment variable, which bundlers inline into the client bundle by design. - A committed
.envfile pushed to a public repository. - A Supabase client initialized with the service role key inside a component that renders in the browser.
Verdent AI's publishable key guide frames the diagnostic cleanly: seeing a publishable key in DevTools is expected; seeing another user's private records through that client is a security problem.
AI tools optimize for the demo, and a hardcoded key is the fastest route to a working demo. (Source: Fencer.dev)
What can an attacker do with a leaked service role key?
A leaked service role key hands an attacker full read and write access to every table in the project, with Row Level Security out of the way. User rows, other tenants' records, auth metadata — nothing in the database requires a permission the attacker lacks.
Regarding blast radius, the key carries no row scoping and no per-user identity. One paste into a public repo or one inline into a client bundle converts the entire database into a public API. Supabase's docs classify browser use of secret keys as never-safe for exactly this reason.
Cleanup follows a fixed order: rotate the key in the Supabase dashboard, redeploy every server that held the old one, then audit logs for queries you did not make. Rotation kills the leaked string; the audit answers what the attacker already copied.
DevMeth checks service-role key exposure as one of the 48 known AI-code failure patterns, with gitleaks as the secrets-detection engine. The free tier runs the 10 critical checks (C1, C2, C3, C5, C6, C7, C8, C9, C13, C25). A pre-flight check, not a pentest — the scan reports whether the pattern is present in the repo or the live bundle.
Where should the service role key live?
Server-side environments are the only correct home: a Next.js route handler or server component, a Supabase Edge Function, a backend worker, a CI job. Supabase's Edge Functions docs mark SUPABASE_SECRET_KEYS as safe in Edge Functions and never safe in a browser.
Regarding client bundles, the test is mechanical. Search the repo for the key string, build the app, then search the output bundle for the same string. The key must appear in zero client artifacts. Variables without a public prefix stay server-side in Next.js; variables with NEXT_PUBLIC_ or VITE_ prefixes reach the bundle by design.
Supabase's Edge Functions secrets documentation draws the same boundary for functions: secret keys are safe in Edge Functions, never in a browser. DevMeth's secrets scanning hub collects the leak paths — committed .env files, client bundles, hardcoded strings — with the fix for each.
My rule for AI-built repos: the service role key appears in exactly one kind of file, a server-side environment file or secret manager, and that file never reaches a browser or a git history.
Supabase's docs mark secret keys safe in Edge Functions and never safe in a browser. (Source: Supabase docs)
Are Supabase's new API keys changing this rule?
Supabase is deprecating the anon and service_role keys by the end of 2026 and replacing them with publishable (sb_publishable_...) and secret (sb_secret_...) keys. The boundary survives the migration: publishable keys serve browsers, secret keys serve servers, and the service-role discipline carries over unchanged.
Supabase's migration guide lays out the timeline and the new formats. Regarding the 2026 migration, the practical moves for an AI-built app match today's discipline: audit where the legacy keys appear, move any service-role usage server-side, and rotate anything that touched a client bundle or a public repo.
The new names remove one classic mistake — the legacy service_role string reads scarier than anon, so models copy the wrong one into client code less often — but the structural rule stands. A secret key in a browser is the same failure under a newer prefix.
DevMeth's Supabase security hub collects the Supabase failure patterns — RLS disabled, service-role key exposure, weak JWT secrets — with the fixes for each.
The prefixes change at the end of 2026; the rule — secret keys never touch a browser — does not. (Source: Supabase docs)
About the Author: Nick Thorp is the founder of DevMeth, the pre-flight security check for AI-built apps. He also built Proven Duty, file-review tooling for UK advice firms.
Frequently asked questions
What is the Supabase service role key?
A secret API key that authenticates a trusted backend component with admin-level access to a Supabase database. Supabase issues public keys for browsers and secret keys for servers; the service role key is the secret class, and Supabase's docs restrict it to the backend.
Why does the service role key bypass Row Level Security?
Row Level Security policies scope each query to the rows a user deserves. Supabase designed the service_role role for trusted server code, so its queries skip policy evaluation. The server is expected to enforce access control before the query runs.
How does the service role key end up in the browser?
AI coding tools frequently hardcode API keys in frontend JavaScript, where browser dev tools expose them. Common routes: a NEXT_PUBLIC_ or VITE_ variable the bundler inlines, a committed .env file, or a Supabase client initialized with the secret key in a browser component.
What can an attacker do with a leaked service role key?
Read and write every table with Row Level Security out of the way. The key carries no row scoping or per-user identity, so one leak converts the database into a public API. Cleanup means rotating the key, redeploying servers, and auditing logs.
Where should the service role key live?
Server-side only: a Next.js route handler or server component, a Supabase Edge Function, a backend worker, or a secret manager. Supabase's docs mark SUPABASE_SECRET_KEYS safe in Edge Functions and never safe in a browser. Client bundles must contain zero copies of the string.
Are Supabase's new API keys changing this rule?
Supabase deprecates the anon and service_role keys by the end of 2026, replacing them with sb_publishable_ and sb_secret_ keys. The boundary survives: publishable keys serve browsers, secret keys serve servers. Audit legacy key usage and move service-role work server-side before the deadline.