Is It Safe to Expose Your Supabase Anon Key? Yes · With One Condition
Quick answer: Yes, exposing your Supabase anon key is safe because it is designed to be public. However, this is only true if Row Level Security (RLS) is enabled on every table. Without RLS, the anon key allows anyone to read your entire database. Never expose the service_role key.
By Daniel A · Kraftwire Software
· 9 min readIs It Safe to Expose Your Supabase Anon Key?
Yes, it is safe to expose your Supabase anon key because it is designed to be public and is included in every frontend bundle by design. The one condition is that Row Level Security (RLS) must be enabled on every table in your database. The anon key does not grant access to your data; it merely identifies the request as coming from an anonymous user. The actual security boundary is the RLS policy you define in Postgres. If RLS is disabled, the anon key becomes a skeleton key for your entire database.
What Is the Difference Between the Anon Key and the service_role Key?
Understanding the distinction between these two keys is the most critical part of Supabase security. Many developers mistakenly treat all API keys as secrets, leading to unnecessary panic or, worse, dangerous exposure of the wrong key.
The anon key
The anon key is a JSON Web Token (JWT) that contains a specific payload: "role": "anon". When your frontend makes a request to Supabase, this key tells the PostgREST layer to execute the query using the anon database role. This is the lowest-privilege role available. It is intended to be used in client-side code, such as in Bolt.new or Lovable projects. Because it is shipped to the browser, any visitor can find it in the network tab or by inspecting the source code. This is not a vulnerability; it is the intended architecture of a "Tier 0" or "vibe-coded" application.
The service_role key
The service_role key is also a JWT, but its payload contains "role": "service_role". In the Supabase ecosystem, this role is a "super user" that bypasses Row Level Security entirely. It is designed for administrative tasks, such as running migrations, background jobs, or server-side logic in Edge Functions. If this key is exposed in your frontend, an attacker can read, delete, or modify every row in your database regardless of your RLS policies.
Why Is a Public Anon Key Fine When RLS Is On?
Row Level Security is a native Postgres feature that filters every query based on the identity of the user making the request. When you enable RLS, you are telling the database: "Do not return any data unless a specific policy allows it."
In this scenario, if an attacker uses your public anon key to query the profiles table, the database checks the policy. Since the attacker is not logged in (auth.uid() is null), the query returns zero rows. The anon key is merely the "entry ticket" to the theater; the RLS policies are the security guards checking if you actually have a seat reserved. For a deeper dive into how to write these guards, see our guide on RLS policies explained.
What Happens If a Table Has No RLS?
This is where the "one condition" becomes a critical failure point. If you create a table and forget to enable RLS, that table is public to anyone with your project URL and your anon key. Because AI tools like Cursor or Windsurf often prioritize speed and "vibes" over security boilerplate, they may skip the enable row level security command.
If RLS is off, an attacker can use a simple curl command to dump your data:
If the users table lacks RLS, this command returns every user record, including emails and metadata. This is why SimplyScan checks for missing RLS on every scan · it is the single most common way vibe-coded apps leak data.
The Ultimate Vibe Coding Security Checklist: Ship AI Apps Safely
If you are building with Replit, v0, or FlutterFlow, follow this checklist to ensure your public keys don't become public liabilities:
- Verify RLS on Every Table: Never assume your AI agent enabled it. Run
SELECT relname, relrowsecurity FROM pg_class WHERE relkind = 'r';in your SQL editor to check. - Use Least-Privilege Policies: Avoid
using (true)policies. EnsureINSERTandUPDATEoperations are restricted to the owner usingauth.uid(). - Audit Environment Variables: Ensure your
service_rolekey is not prefixed withNEXT_PUBLIC_orVITE_. These prefixes force the build tool to embed the secret in the client bundle. - Check Storage Buckets: RLS applies to files too. Ensure your Supabase Storage buckets have policies that prevent unauthorized downloads.
- Scan for Exposed Secrets: Use a secret scanner to ensure no
sb_secretkeys have been committed to your repository.
How Do You Check Which Key You Shipped?
If you are unsure which key is currently in your production app, you can verify it in seconds without looking at your source code.
Step 1: Inspect the Network Traffic
Open your application in a browser, press F12 to open Developer Tools, and go to the Network tab. Refresh the page and look for any request to your Supabase URL. Click on the request and look for the apikey or Authorization header.
Step 2: Decode the JWT
Copy the long string starting with eyJ. This is your key. Paste it into our JWT debugger. Look at the "Payload" section:
- If
"role": "anon", you are safe (provided RLS is on). - If
"role": "service_role", you have a critical leak.
Step 3: Check the New Key Format
Supabase has introduced a clearer naming convention for newer projects. Keys starting with sb_publishable_ are equivalent to the anon key and are safe for the frontend. Keys starting with sb_secret_ are the service role and must remain on the server.
What Should You Do If You Shipped the service_role Key?
If you discover the service_role key in your frontend, you must act immediately. This is a live data breach.
- Rotate the Key: Go to your Supabase Dashboard > Settings > API and click "Rotate Key" for the service_role. This immediately kills the old key.
- Update Server Environments: Update the environment variables in your Vercel, Netlify, or GitHub Actions settings with the new key.
- Purge Git History: If the key was committed to a public repo, rotating it is not enough to hide the mistake, though it stops the access. Use our guide on how to remove secrets from git history to clean up the trail.
- Audit Logs: Check your Supabase logs for unusual activity or bulk data exports that occurred while the key was exposed.
When Should You NOT Worry?
You do not need to rotate your key or panic if:
- A security scanner flags your "exposed" anon key but doesn't check the role.
- Your anon key is visible in a public GitHub repository (as long as RLS is active).
- A user sees the anon key in their browser's local storage or network tab.
Often, the "exposed key" warning is a false positive from generic scanners that don't understand the Supabase architecture.
How Can You Verify Your Project in Thirty Seconds?
Manually checking every table for RLS and every environment variable for leaks is time-consuming. SimplyScan provides a free, comprehensive scan that grades your site across 8 dimensions, including security, speed, and AEO (AI Visibility). Our engine specifically detects missing Supabase RLS and exposed service_role keys, giving you a pass/fail grade in about 30 seconds.
Don't guess if your "vibe" is secure. Run a free security scan today to verify your RLS coverage and protect your users.
FAQ
Can someone do damage with just my Supabase project URL and anon key?
Only if you have disabled Row Level Security (RLS) on your tables. With RLS enabled and proper policies, the anon key only allows access to data you have explicitly made public. However, if RLS is off, an attacker can use the anon key to read every row in your database via the REST API.
Should I rotate my Supabase anon key if it appears in a public GitHub repo?
Generally, no. The anon key is intended to be public and is already accessible to anyone who visits your website. Rotating it provides no security benefit as the new key will also be public. The only exception is if you are also rotating your project URL or if you suspect a broader configuration issue.
Is enabling RLS enough, or do I also need good policies?
Enabling RLS is only the first step. You must also define policies that follow the principle of least privilege. For example, a policy that uses true for all operations is just as dangerous as having RLS disabled. You should scope SELECT and ALL operations to specific authenticated users.
What are the new sb_publishable and sb_secret Supabase keys?
Supabase introduced these prefixes to make it easier to identify key types. sb_publishable_ keys are the modern version of the anon key and are safe for frontend use. sb_secret_ keys are the modern version of the service_role key and must never be exposed to the client.
How does a service_role key usually end up in a frontend bundle?
The most common cause is using an environment variable prefix like NEXT_PUBLIC_ or VITE_ for the service role key. These prefixes tell the build tool to include the variable in the JavaScript bundle sent to the browser. Always keep the service role in server-side-only variables.
How can I test my Supabase RLS coverage without writing code?
You can use SimplyScan to perform an automated check of your project's security posture. The scanner identifies tables where RLS is missing and flags if a high-privilege key has been leaked in your frontend code, providing a clear report in under 30 seconds.
Frequently asked questions
Can someone do damage with just my Supabase project URL and anon key?
Only if you have disabled Row Level Security (RLS) on your tables. With RLS enabled and proper policies, the anon key only allows access to data you have explicitly made public. However, if RLS is off, an attacker can use the anon key to read every row in your database via the REST API.
Should I rotate my Supabase anon key if it appears in a public GitHub repo?
Generally, no. The anon key is intended to be public and is already accessible to anyone who visits your website. Rotating it provides no security benefit as the new key will also be public. The only exception is if you are also rotating your project URL or if you suspect a broader configuration issue.
Is enabling RLS enough, or do I also need good policies?
Enabling RLS is only the first step. You must also define policies that follow the principle of least privilege. For example, a policy that uses true for all operations is just as dangerous as having RLS disabled. You should scope SELECT and ALL operations to specific authenticated users.
What are the new sb_publishable and sb_secret Supabase keys?
Supabase introduced these prefixes to make it easier to identify key types. sb_publishable_ keys are the modern version of the anon key and are safe for frontend use. sb_secret_ keys are the modern version of the service_role key and must never be exposed to the client.
How does a service_role key usually end up in a frontend bundle?
The most common cause is using an environment variable prefix like NEXT_PUBLIC_ or VITE_ for the service role key. These prefixes tell the build tool to include the variable in the JavaScript bundle sent to the browser. Always keep the service role in server-side-only variables.
How can I test my Supabase RLS coverage without writing code?
You can use SimplyScan to perform an automated check of your project's security posture. The scanner identifies tables where RLS is missing and flags if a high-privilege key has been leaked in your frontend code, providing a clear report in under 30 seconds.