All articles

Web Development

Is Your Lovable App Leaking Data? Check Your Supabase RLS in 2 Minutes

Many Lovable and Bolt apps ship with Supabase tables anyone can read. Here is how to check yours in two minutes and fix Row Level Security properly.

7 min read
Padlock resting on a computer keyboard
Photo: Sasun Bughdaryan on Unsplash

If your app was built with Lovable, Bolt, v0 or another AI tool and uses Supabase, there is one question worth answering today: can a stranger read your database?

It sounds dramatic, but it is one of the most common problems in AI-built apps, and it has already caused real data leaks. Checking takes a couple of minutes, and the fix is well understood.

Why this happens

A Supabase app ships two things to every visitor's browser: your project URL and the anon (public) key. That is by design. The anon key is meant to be public (see Supabase's guide to API keys).

What keeps your data safe is Row Level Security (RLS): rules in your database that decide which rows each user may read or change. If RLS is switched off on a table, or its policies allow everyone, then anyone who opens your site can copy the anon key from the browser and query that table directly, without logging in.

AI tools often get this wrong in two ways. Either a table is created without RLS, or, when the app shows an error like a 401 or an empty list, the "fix" is to disable RLS or add a policy that allows everything. The error disappears, and so does your protection.

The 2-minute check

1. Look for tables without RLS

In the Supabase dashboard, open Table Editor. Tables without protection are flagged as RLS disabled. For a complete list, open the SQL Editor and run:

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

Any table showing rowsecurity = false is readable and writable with your public key, unless you have locked it down another way. Treat every one of them as exposed.

2. Look for policies that let everyone in

RLS can be switched on and still be wide open. List your policies:

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

Be suspicious of any policy where qual or with_check is simply true, especially for insert, update or delete, or where the role is anon or public on tables with private data. using (true) means "every row, for everyone it applies to".

3. Run the Security Advisor

Supabase has a built-in Security Advisor in the dashboard (under Advisors). It flags tables with RLS disabled, views that bypass RLS, and other common mistakes. Fix everything it marks as an error.

4. Confirm from the outside

For your own project only: while logged out, request a table through Supabase's API with just the anon key. If rows come back for data that should be private, it is exposed:

curl "https://YOUR-PROJECT.supabase.co/rest/v1/profiles?select=*" \
  -H "apikey: YOUR_ANON_KEY"

An empty list ([]) for a logged-out request is what you want for private tables.

How to fix it properly

Switch RLS on

alter table public.profiles enable row level security;

With RLS on and no policies, nobody can read the table through the API. That is safe, but your app will stop showing data until you add the right policies.

Add policies that match who should see what

The most common pattern is "users can only see and change their own rows". For a table with a user_id column linked to the logged-in user:

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

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

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

Truly public data, like published blog posts or a product catalogue, can have a read-only policy for everyone. Keep writes restricted.

Do not "fix" errors by removing protection

  • Never disable RLS to make an error go away. Find out which policy is missing instead.
  • Never put the service_role key in the frontend. It bypasses RLS completely. In Vite apps, anything in a VITE_ variable ends up in the browser, so the service role key must never be one of them. Use it only in server code or Supabase Edge Functions.
  • Avoid blanket using (true) policies on anything that is not meant to be public.

Places people forget

  • Views. By default a view runs with its owner's permissions and can bypass RLS on the tables behind it. On recent Postgres versions, create views with with (security_invoker = true) so they respect RLS.
  • Database functions marked security definer also bypass RLS. Make sure they check who is calling.
  • Storage buckets. Uploaded files have their own access policies. Check whether buckets with private files are public.
  • Old test tables left over from early prompts. Delete what you do not use.

Test like an attacker, then like a user

  1. Logged out: private tables return nothing.
  2. Logged in as user A: you see only user A's data.
  3. Logged in as user B: you cannot see or change user A's rows, even by guessing IDs.
  4. The app still works normally for both users.

If your data was already exposed

Fix the policies first, then rotate any keys or secrets that were stored in exposed tables, review your logs for unusual access, and, depending on what was exposed and where your users are, check whether you need to notify them under the data protection laws that apply to you.

Want us to check it for you?

We audit and fix Supabase security in apps built with Lovable, Bolt, Base44, Cursor and other AI tools: RLS policies, exposed keys, views, functions and storage, without breaking the features your users rely on. See our Supabase fixes, or message us on WhatsApp for a free check of your project.

Frequently asked questions

Is it a problem that my Supabase anon key is visible in the browser?

No, that key is designed to be public. It only becomes a problem when Row Level Security is missing or too permissive, because then the public key can read or change your data.

Why did my app break after I enabled RLS?

With RLS on and no matching policy, Supabase returns no rows. Add policies that allow each user to access the rows they should, and the app will work again, safely.

Does Lovable set up RLS automatically?

Lovable has added RLS guidance and checks, but generated policies can still be missing or too broad, especially in projects that were changed by many prompts. Always verify with the checks above.

Is the service_role key safe in an environment variable?

Only on a server or in Edge Functions. In a Vite or React frontend, environment variables are bundled into the code sent to the browser, so the service_role key must never be there.

  • #Supabase
  • #Security
  • #Lovable
  • #RLS
  • #vibe coding

Is your website or app broken, slow or unfinished?

Send us a message describing the problem. We'll look at it and tell you honestly what it takes to fix, or quote you for a new build.

  • Free assessment and a clear, fixed quote before work starts
  • Direct contact with the developer working on your project
  • Full source code and access handed over to you
  • Support after launch so fixes stay fixed