Launchpad Studio
ServicesPricingBlogAboutFAQ
Launchpad Studio

Technical services for startups, solo founders, and growing teams.

Services

  • Launch-Readiness Audit
  • Quick Security Check
  • Technical Assessment
  • Startup Package
  • Custom Workflows
  • AI Services

Company

  • About
  • Blog
  • Contact

Resources

  • FAQ
  • Pricing

© 2026 Launchpad Studio. All rights reserved.

Launchpad Studio is a trading name of Farmoi Oy · Y-tunnus 3519906-4

Privacy Policy
Is Your Supabase App Actually Private? A 10-Minute Row Level Security Check | Launchpad Studio
Launchpad Studio
ServicesPricingBlogAboutFAQ
Launchpad Studio

Technical services for startups, solo founders, and growing teams.

Services

  • Launch-Readiness Audit
  • Quick Security Check
  • Technical Assessment
  • Startup Package
  • Custom Workflows
  • AI Services

Company

  • About
  • Blog
  • Contact

Resources

  • FAQ
  • Pricing

© 2026 Launchpad Studio. All rights reserved.

Launchpad Studio is a trading name of Farmoi Oy · Y-tunnus 3519906-4

Privacy Policy
Launchpad Studio
ServicesPricingBlogAboutFAQ
Launchpad Studio

Technical services for startups, solo founders, and growing teams.

Services

  • Launch-Readiness Audit
  • Quick Security Check
  • Technical Assessment
  • Startup Package
  • Custom Workflows
  • AI Services

Company

  • About
  • Blog
  • Contact

Resources

  • FAQ
  • Pricing

© 2026 Launchpad Studio. All rights reserved.

Launchpad Studio is a trading name of Farmoi Oy · Y-tunnus 3519906-4

Privacy Policy
Launchpad Studio
ServicesPricingBlogAboutFAQ
Supabase
Security
AI-built apps

Is Your Supabase App Actually Private? A 10-Minute Row Level Security Check

Your Supabase public key is visible to every visitor by design. Row Level Security is the only thing keeping your users' data private. Here is how to check it in ten minutes, without being a database expert.

October 11, 2026
Mehran Rafiee

If your app was built with Lovable, Bolt, Cursor, or a similar tool, there is a good chance it uses Supabase for its database and logins. Supabase is a solid product. But it works in a way that surprises people: your app's browser code talks to the database directly, using a key that every visitor can see.

That is by design, and it is safe, as long as one thing is true: Row Level Security (RLS) is switched on for your tables, with sensible rules. If it isn't, anyone who opens your site's developer tools can read, and often change, everything in those tables.

This post shows you how to check that in about ten minutes. You don't need to be a database expert.

The idea in one minute

  • The public key (called the anon key, or the publishable key in newer projects) is in your app's code on purpose. Everyone can see it.
  • Row Level Security is a set of rules inside the database that decide which rows each user may read or change. With it on and the right rules, the public key can only reach what that user is allowed to see.
  • With it off, the public key can reach the whole table.
  • The secret key (service_role, or the secret key in newer projects) skips all those rules. It must only ever live on a server, never in the browser.

Step 1: Try to read your own data while logged out

This is the fastest test, and it is exactly what an attacker would do. Replace the three placeholders with your project's address, a table name from your app (for example profiles or orders), and the public key from your project's API settings:

curl "https://YOUR-PROJECT.supabase.co/rest/v1/YOUR_TABLE?select=*&limit=5" \
  -H "apikey: YOUR_PUBLIC_KEY" \
  -H "Authorization: Bearer YOUR_PUBLIC_KEY"
  • You get rows back: that table is open to the whole internet. Stop and fix it before anything else.
  • You get an empty list [] or an error: good, as long as the table actually has data in it. An empty table gives [] whether it's protected or not.

Repeat it for every table that holds anything personal: users, orders, messages, files, settings.

Step 2: List every table that has no protection

In the Supabase dashboard, open the SQL Editor and run:

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

Every table with rowsecurity = false has RLS switched off. Tables created through the SQL editor or by a migration often start that way, and AI tools create tables through migrations. The dashboard's Security Advisor also flags these tables, so check it too.

Step 3: Read the rules on the tables that are protected

RLS being on is not enough. Run this to see every rule:

select tablename, policyname, cmd, roles, qual, with_check
from pg_policies
where schemaname = 'public'
order by tablename;

Look for these four problems:

  1. A rule that is just true. If qual is true, the rule lets everyone through. It's the AI tool's way of making an error message go away.
  2. Rules for anon that write data. The anon role is anyone who isn't logged in. Insert, update, and delete rules for it are rarely what you want.
  3. No with_check on insert or update rules. Without it, a user can write a row that belongs to somebody else.
  4. Rules that trust user_metadata. Users can edit their own user_metadata. Never base permission on it, for example "is admin". Use app_metadata or a roles table that only your server can change.

What a correct basic rule looks like

For a table where each row belongs to one user, via a user_id column:

alter table notes enable row level security;

create policy "Users read their own notes"
  on notes for select
  to authenticated
  using ( (select auth.uid()) = user_id );

create policy "Users insert their own notes"
  on notes for insert
  to authenticated
  with check ( (select auth.uid()) = user_id );

create policy "Users update their own notes"
  on notes for update
  to authenticated
  using ( (select auth.uid()) = user_id )
  with check ( (select auth.uid()) = user_id );

create policy "Users delete their own notes"
  on notes for delete
  to authenticated
  using ( (select auth.uid()) = user_id );

The pattern is always the same: say who the rule is for, and compare the logged-in user's ID to the owner of the row.

Three other places data leaks

  • Views. A view reads its tables with its creator's permissions by default, which skips your rules. On recent Postgres versions, create views with security_invoker = true so they respect the caller's rules.
  • Storage buckets. Files have their own switch: a bucket marked public serves every file in it to anyone with the link. Check that invoices, documents, and uploads are in private buckets with rules of their own.
  • The secret key in the browser. Search your deployed site's files for the secret key. If it's there, rotate it in the dashboard first, then remove it from the code. Deleting it from the code alone is not enough.

Fixing this without breaking your app

One trap: switching RLS on for a table that has no rules blocks all access through the public key, including your own app's. The app will look broken. So:

  1. Write the rules first, ideally on a copy of your database or a test project.
  2. Switch RLS on and test with two separate accounts: user A creates something, then user B must not be able to see or change it.
  3. Repeat the logged-out test from Step 1.
  4. Only then apply the change to your live project.

Your AI tool can write these rules for you. Treat what it writes as a draft: read every rule, and run the tests above. The mistake to watch for is a rule written to silence an error rather than to protect the data.

Where this fits

Database rules are check 9 of 33 in the full pre-launch checklist. The others cover secrets, logins, payments, and costs. If you'd rather have an engineer go through all of it and hand you a ranked list of what to fix, that's the launch-readiness audit.

Launchpad Studio

Technical services for startups, solo founders, and growing teams.

Services

  • Launch-Readiness Audit
  • Quick Security Check
  • Technical Assessment
  • Startup Package
  • Custom Workflows
  • AI Services

Company

  • About
  • Blog
  • Contact

Resources

  • FAQ
  • Pricing

© 2026 Launchpad Studio. All rights reserved.

Launchpad Studio is a trading name of Farmoi Oy · Y-tunnus 3519906-4

Privacy Policy