How do I check if my Supabase app is secure?

Direct answer

Check that row-level security is enabled on every table holding user data, and that your service role key isn't in client-side code. Supabase exposes tables over a REST API as soon as they exist. Without RLS policies that API is open, and the anon key that's meant to be public gives anyone access to everything.

Muhammad HasanUpdated

Supabase gives you a Postgres database with a REST API in front of it. Create a table and it's queryable over HTTP immediately. That's the appeal, and it's also the failure mode.

RLS is the security model

The anon key that ships in your frontend is designed to be public. It identifies the project, not the user, and it grants no privileges by itself. What decides who can read what is row-level security policies on each table.

Without RLS enabled, a table is readable by anyone holding the anon key. Which is anyone who opened your site and looked at the network tab.

Check it in the dashboard under Authentication → Policies, or directly:

select tablename, rowsecurity
from pg_tables
where schemaname = 'public';

Anything with user data and rowsecurity false is exposed right now.

Enabling it isn't finishing it

Enabling RLS without policies blocks everything, which usually surfaces as the app breaking. The next step is where mistakes happen, a permissive policy gets added to make things work again and never gets tightened.

alter table profiles enable row level security;

create policy "own profile only"
on profiles for select
using (auth.uid() = user_id);

Read the policies you already have rather than assuming. A policy using true as its condition is RLS that permits everything.

Policies are also per-operation. Select, insert, update and delete each need their own. Locking down reads while leaving updates open is a common half-fix.

The service role key

Supabase issues two keys. The anon key is public by design. The service role key bypasses RLS entirely and must never reach the browser.

On Next.js that means it goes in a server-only environment variable with no NEXT_PUBLIC_ prefix. Search your deployed bundle for service_role and for the key itself. If it's there, rotate it immediately, anything already shipped is compromised regardless of what you change afterwards.

Why this keeps happening

Lovable, Replit and several other AI builders use Supabase as their default backend. They generate the schema and the frontend together, and RLS is a separate deliberate step that a prompt describing a feature won't produce.

This isn't hypothetical. CVE-2025-48757, rated CVSS 9.3, was assigned after more than 170 production Lovable applications were found with fully readable and writable databases. Anyone with the anon key, which is in the frontend by design, could read, modify or delete any row.

Two patterns dominated the 24-app study: credentials sitting in client-side code, and queries that never checked ownership. A Supabase project puts both in the same place.

Quick audit

  1. List tables where rowsecurity is false

  2. Read the policies on the rest and look for true conditions

  3. Confirm policies exist for insert and update, not only select

  4. Search the deployed bundle for service_role

  5. Test it. Open a private window, hit your REST endpoint with the anon key, see what comes back

That last one is the only step that tells you the truth rather than what the config says.

Go deeper

Security for AI-generated code

Related answers

See what your own repository returns

Connect a repo and run a scan. No credit card, no pipeline changes.

Get started for free