Guide Lovable and Supabase

How do I fix Supabase RLS in a Lovable app?

Short answer

Open Supabase's Security Advisor and fix every "RLS disabled in public" error. Enable Row Level Security on each table, then add one policy per action (select, insert, update, delete) that limits rows to their owner with auth.uid(). Test while logged out with only the public key, and keep the secret key out of the frontend.

RLS means Row Level Security: rules in your Supabase database that decide, row by row, who can read or change data. Lovable apps that use Supabase rely on it to keep one user's data away from everyone else.

What does the Supabase Security Advisor flag?

In your Supabase dashboard, open Advisors, then Security Advisor. If a table in the public schema has Row Level Security switched off, the Advisor shows an error (lint 0013) with this warning: Anyone with your project URL can read, edit, and delete all data in this table because Row-Level Security is not enabled. (Supabase: Database Advisors)

Fix every error first, then look at the warnings.

How do I turn on RLS for every table?

In the Supabase SQL editor, this query lists every table in public that still has RLS off:

select c.relname as table_name
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public'
  and c.relkind = 'r'
  and not c.relrowsecurity;

For each table it lists, turn RLS on. Here's the line for a table called orders:

alter table public.orders enable row level security;

Supabase says: Once RLS is enabled, no data is accessible through the API when using a publishable key, until you create policies. (Supabase: Row Level Security)

Our guidance at PatchAndShip: turn RLS on and add the policies from the next step in the same change, so the table never ships without them and your app doesn't go blank.

What policies does each table need?

One policy per action: read (select), create (insert), edit (update) and delete. Each one lets a signed‑in user touch only rows they own. This example assumes each row stores its owner in a user_id column. Change the names to match your table.

create policy "orders_select_own" on public.orders
  for select to authenticated
  using ((select auth.uid()) = user_id);

create policy "orders_insert_own" on public.orders
  for insert to authenticated
  with check ((select auth.uid()) = user_id);

create policy "orders_update_own" on public.orders
  for update to authenticated
  using ((select auth.uid()) = user_id)
  with check ((select auth.uid()) = user_id);

create policy "orders_delete_own" on public.orders
  for delete to authenticated
  using ((select auth.uid()) = user_id);

The edit policy checks the row twice. In Supabase's explanation, using decides which existing rows can be updated, and with check decides what the resulting row may look like, which stops a user from reassigning user_id to someone else. If with check is left out, using is applied to both (Supabase: Row Level Security; PostgreSQL: CREATE POLICY). Our guidance: write both, so the rule for the new row is explicit.

The policies wrap the call as (select auth.uid()). Supabase says this makes Postgres run an initPlan that caches the result per statement instead of calling the function on each row, which improves RLS performance. It only works when the result doesn't depend on row data (Supabase: RLS performance and best practices).

Avoid policies that let everyone in. The Security Advisor warns about always‑true conditions such as USING (true) (lint 0024, Supabase: Database Advisors).

Do I need grants as well as RLS?

Yes. Supabase explains that grants control whether a role can reach a table at all, and RLS controls which rows it can access. Supabase's advice: Use both controls for every exposed object. (Supabase: Securing your API)

Supabase also notes: If the grant is missing, Postgres rejects the query before RLS comes into play. (Supabase changelog) From October 30, 2026, Supabase plans to require an explicit grant for new tables in the public schema of existing projects. Our guide to Supabase error 42501 and the October 30 change shows the safe grants.

What about views the Advisor marks as an error?

The Security Advisor flags a “security definer view” as an error (lint 0010, Supabase: Database Advisors). Views bypass RLS by default because they run with the view owner's permissions, and in Supabase views are usually created by the postgres user (Supabase: Row Level Security; PostgreSQL: CREATE VIEW).

Our fix at PatchAndShip: on Postgres 15 and above, recreate the view with security_invoker turned on, so it follows the RLS policies of the tables underneath it.

create view public.public_profiles
with (security_invoker = true) as
select id, display_name, avatar_url from public.profiles;

On older Postgres versions, Supabase says to revoke access to the view from anon and authenticated, or put it in an unexposed schema (Supabase: Row Level Security).

How do I test it while logged out?

Only test an app you own. Open your live app, sign out, and find the public key your app already sends to Supabase (in your browser's developer tools, it's the apikey header on requests to supabase.co). Then ask Supabase for rows the way a stranger would:

curl "https://YOUR_PROJECT.supabase.co/rest/v1/orders?select=*&limit=5" \
  -H "apikey: YOUR_PUBLIC_KEY"

Our guidance: a private table should return an empty list or an error. If it returns rows, anyone can read them. Run it for every table, then sign in as one user and confirm you can't open another user's records.

Which Supabase key must never be in my frontend?

The secret key (or the older service_role key). Supabase says: A secret key bypasses every Row Level Security policy you have. Never put one in a browser, a shipped application, or source control. The publishable and anon keys are designed to be public (Supabase: API keys).

If your project uses Vite, be careful with variable names. Vite says: Variables prefixed with VITE_ will be exposed in client-side source code after Vite bundling. (Vite: Env Variables and Modes) A secret key must never sit in a VITE_ variable.

Vite writes VITE_ values into your code at build time. If you change one, for example after rotating a key, rebuild and redeploy the app; the live site keeps the old value until you do (Vite: Env Variables and Modes; Netlify: Deploy your Bolt app to Netlify).

When should I get help?

If the Advisor still shows errors after these steps, or you're not sure which rows each user should see, PatchAndShip can fix it for you. The Security Fix ($149) covers RLS fixes on all tables, storage bucket policies, a key scan with rotation guidance and a short report. Security Fix: every critical issue on our checklist fixed within 48 hours of working access. Otherwise, a full refund if you ask within 14 days of purchase.

Not sure yet? PatchAndShip's free mini check is passive: it looks only at the public homepage, its scripts and its security headers (for exposed secret keys, missing headers and publicly listed storage links), and nothing else: no database queries, no logins, no changes. It returns a one‑page summary within 24 hours.

Prefer a list to work through? Use the free Supabase RLS checklist.

What else do Lovable founders ask about RLS?

Is the anon key safe in my frontend?

Yes. Supabase says publishable and anon keys are designed to be public. Row Level Security is what protects your data. The key that must stay out of the frontend is the secret key, because Supabase says "A secret key bypasses every Row Level Security policy you have."

Does Lovable's security scan cover this?

Partly. Lovable's docs say its Quick scan checks for "tables without per-record access control (row-level security)" and access "rules that let everyone through". Lovable also says its security tools "do not replace a thorough security review."

What's changing on October 30, 2026?

Supabase plans that, from October 30, 2026, new tables you create in the public schema of existing projects will need an explicit grant before the Data API can see them. Existing tables keep their current grants, and RLS behavior is unchanged. As of October 14, 2026, this change has not reached existing projects.

Sources: Supabase: API keys, Lovable Docs: Security overview, Supabase changelog.

Where do these facts come from?

  1. Supabase Docs: Database Advisors. Lint 0013 (RLS disabled in public), lint 0024 (permissive policy), lint 0010 (security definer view).
  2. Supabase Docs: Securing your API. Grants and RLS.
  3. Supabase changelog: Breaking Change: Tables not exposed to Data and GraphQL API automatically. Published 28 Apr 2026.
  4. Supabase Docs: API keys. Secret, publishable and anon keys.
  5. Vite Docs: Env Variables and Modes. VITE_ variables.
  6. Lovable Docs: Security overview. What Lovable's scan checks.
  7. Supabase Docs: Row Level Security. Policy syntax, no data until policies exist, update policies, views.
  8. Supabase Troubleshooting: RLS performance and best practices. Wrapping auth.uid() in a select.
  9. PostgreSQL Docs: CREATE POLICY and CREATE VIEW. Update policies and view permissions.
  10. Netlify Knowledge Base: Deploy your Bolt app to Netlify. Rebuild after changing variables.

All sources checked on . PatchAndShip's own guidance and service details are labeled as ours.