Stack Security GuidesSupabase security

Is Supabase secure?

If you have found your Supabase anon key in your client bundle: that is expected. It is public by design, it identifies your project, and shipping it is how Supabase is meant to work. What protects your data is Row Level Security — which is opt-in, and off by default on new tables. Get RLS right and Supabase is solid; skip it and the public key everyone can read is enough to download your database. The one key that must never reach the browser is service_role, which bypasses RLS entirely.

Run a free scan

Get a graded security report for your Supabase app in under a minute.

Scan my Supabase app

Why Supabase apps are commonly at risk

  • The anon key is public by design and present in every client — Row Level Security, not the key, is what protects your data.
  • RLS is disabled by default on new tables, so a table is open until you turn it on.
  • A policy of `USING (true)` looks like protection while allowing everything.
  • The service_role key bypasses RLS entirely and is catastrophic if it reaches the browser.
  • Storage buckets and edge functions have their own access rules that are easy to leave open.

Top security risks in Supabase apps

Tables with RLS disabled

High

Any table without RLS is fully readable — and often writable — through the REST API using only the public anon key.

service_role key exposed to the client

High

The service_role key ignores every RLS policy. If it appears in client code or a `NEXT_PUBLIC_`/`VITE_` variable, your entire database is exposed.

Permissive `USING (true)` policies

High

RLS enabled with a `true` predicate passes a casual check while granting exactly the access that having no policy would.

Public storage buckets

Medium

Buckets left public expose uploaded files to anyone who can guess the URL; private buckets with signed URLs close that gap.

Unauthenticated edge functions

Medium

Edge functions that don’t verify a JWT can be invoked by anyone, bypassing the protections you put on the tables.

How to fix Supabase security gaps

Enable RLS on every table

Run `ALTER TABLE x ENABLE ROW LEVEL SECURITY` on all tables — including lookup and join tables — not only the obviously sensitive ones.

Write user-scoped policies, never USING (true)

Scope each policy with `auth.uid() = user_id` (or a role check). If you catch a `USING (true)`, treat it as an open table.

Keep service_role strictly server-side

Use the service_role key only in trusted server environments. Grep your client bundle to confirm it never appears there.

Lock down storage and edge functions

Set buckets to private and serve via signed URLs; require and verify a JWT in every edge function.

How do you make a Supabase app rank in Google and AI search?

Securing the app is half the job; the other half is being found. AI engines — ChatGPT, Claude, Perplexity — only cite what their crawlers can read.

AEO lives in your frontend, not Supabase

Supabase is your data layer; whether AI engines can read your app depends on how your frontend renders. Server-render content routes so crawlers receive real HTML.

Allow AI crawlers and ship a sitemap

In robots.txt, allow GPTBot, ClaudeBot and PerplexityBot and point them at your sitemap so answer engines can discover your pages.

Add structured data

Organization, WebSite and Product/SoftwareApplication JSON-LD help engines describe your app accurately.

Check that AI crawlers are not blocked

ShipReady reports which AI crawlers your robots.txt disallows, and whether your host blocks a GPTBot request outright — the access problems worth ruling out before anything else.

Deep dives: AI assistants are blocked from reading your site·Vibe coding security: the real risks in AI-built apps

Supabase security: related guides

FAQ

Is it safe that my Supabase anon key is public?
Yes — it is designed to be. It identifies your project and is protected by Row Level Security. The key being visible is only a problem when RLS is off or a policy is `USING (true)`.
What is the difference between the anon key and the service_role key?
The anon key is public and RLS applies to it: it can only reach rows your policies allow. The service_role key is a superuser credential that bypasses RLS entirely, so it can read, change and delete every row in every table regardless of policy. The anon key belongs in your client; service_role belongs only on a server you control, and never in a NEXT_PUBLIC_ or VITE_ variable.
Where do I find my Supabase anon key?
In the Supabase dashboard under Project Settings → API. The same screen shows the service_role key — copy carefully, because the two sit next to each other and only one of them is safe to put in client code.
What is the single most important Supabase setting?
Row Level Security, enabled on every table with policies that scope rows to the current user. It is the difference between a private database and a public one.
How do I know if my Supabase project is exposed?
From the outside, an exposed table is one that answers a request nobody authenticated. That is exactly what ShipReady tests: it queries your Supabase REST API with the same public anon key your app ships, and reports every table that returns rows. It has no access to your project and cannot read your policies, so it reports the exposure rather than the cause — a table with RLS switched off and a table with a `USING (true)` policy look identical from there, and both need fixing. It also searches your published JavaScript for the service_role key, which should never appear in it.
How does Supabase RLS compare to Firebase Security Rules?
They solve the same problem in different places. Supabase RLS is standard Postgres row-level security: SQL policies attached to tables, scoped with `auth.uid()`. Firebase uses a separate rules language evaluated per request and deployed with the project. The failure modes are mirror images — Supabase RLS is off by default on every new table, so the risk is forgetting to turn it on, while Firebase rules start locked and the risk is loosening them to test mode and never tightening them again. Supabase also has the service_role key, which bypasses policies entirely and has no Firebase equivalent.

Don't guess. Scan your Supabase app now.

100+ checks. 30 seconds. Free — with an AI-ready fix prompt for every critical and high finding.