Is your Supabase service_role key exposed in your frontend JavaScript?
Security basics · 5 min read
The short answer
Supabase gives every project two API keys: anon and service_role. The anon key is meant to be public. It's designed to sit in your frontend, and Row Level Security (RLS) policies decide what it can actually read or write. The service_role key is different: it bypasses RLS entirely. Anyone holding it can read, write, or delete any row in any table, full stop. If that key is sitting in your client-side JavaScript bundle, anyone who opens dev tools has admin access to your database.
How it ends up there by accident
Nobody sets out to ship the service key to the browser. It usually happens one of these ways:
- Copy-pasting an environment variable into a client component in a Next.js app, without noticing it isn't prefixed for server-only use.
- An AI coding assistant generating a "quick fix" that reads
process.env.SUPABASE_SERVICE_ROLE_KEYdirectly inside a file that gets bundled for the browser. - A serverless function meant to run server-side getting imported into a client bundle by accident, dragging its environment access with it.
None of these show an error. The app works. The key just quietly ships to every visitor's browser as part of the JS bundle.
How to check it yourself, right now
Open your site, view page source or open dev tools, and search the loaded JavaScript for service_role or for a long string starting with eyJ (Supabase keys are JWTs). If you find one and can decode it (paste it into any JWT decoder) and it shows "role":"service_role", that key is live and exposed. Rotate it immediately in your Supabase project settings. Rotating invalidates the old key everywhere it's used, including your own backend, so update your server environment variables at the same time.
How Auditly checks this
Auditly's scanner crawls the same client-side JavaScript a browser would load and scans it for exposed secrets: Supabase JWTs (both anon and service_role, distinguished by their decoded role claim), AWS/Stripe/Google API keys, and other embedded credentials. It reports what it found and where, without needing any special access to your codebase, the same way anyone else browsing your site could find it.