Guide
Supabase RLS Checklist for Lovable & Bolt Apps (2026)
Turn on Row Level Security, write owner-only policies, and handle the Oct 30 2026 Data API grant change. Plain steps you can paste into Lovable or Bolt.
.env files. Run the free teaser scan at leakowl.com.Short answer: the Supabase anon key in your frontend is meant to be public. What protects your data is Row Level Security (RLS) plus the right Postgres grants. If RLS is off, anyone with your project URL and anon key can read (and often write) your tables.
1. Why the anon key is public
Lovable and Bolt apps talk to Supabase from the browser using supabase-js and the anon (publishable) key. That key is designed to ship in frontend code. It is not a secret.
Security comes from two layers Postgres checks in order:
- Grants — does this role (
anon,authenticated, orservice_role) have permission to run SELECT / INSERT / UPDATE / DELETE on the table at all? - RLS policies — which rows is that role allowed to see or change?
The service_role key bypasses RLS. It must never appear in browser code, VITE_ env vars, or a committed .env.
2. Find tables marked Unrestricted
In the Supabase Dashboard:
- Open Table Editor.
- Look for tables tagged Unrestricted (or similar "RLS disabled" badges). Those are reachable without row filters once a role has a grant.
- Open Advisors → Security Advisor. Fix every warning that says RLS is disabled or that a table is exposed without policies.
You can also list tables without RLS from the SQL Editor:
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 c.relrowsecurity = false order by 1;
3. Enable RLS with owner-only policies
Treat these three steps as one unit for every user-owned table: grant only what you need, enable RLS, then add policies.
-- Example: notes that belong to the signed-in user -- 1. Grants (see also the Oct 30 2026 section below) grant select, insert, update, delete on public.notes to authenticated; -- Only grant to anon if signed-out visitors truly need that operation: -- grant select on public.notes to anon; -- 2. Turn RLS on alter table public.notes enable row level security; -- 3. Owner-only policies create policy "users can select own notes" on public.notes for select to authenticated using (auth.uid() = user_id); create policy "users can insert own notes" on public.notes for insert to authenticated with check (auth.uid() = user_id); create policy "users can update own notes" on public.notes for update to authenticated using (auth.uid() = user_id) with check (auth.uid() = user_id); create policy "users can delete own notes" on public.notes for delete to authenticated using (auth.uid() = user_id);
- Every table in
publicthat the Data API can reach needs RLS on. - Prefer policies scoped to
auth.uid() = user_id(or an equivalent ownership column). - Do not grant
anonwrite access unless the product truly needs anonymous writes. service_rolekeeps full access and bypasses RLS — use it only on the server (Edge Functions, trusted backends).
4. The Oct 30, 2026 Data API change
Source: Supabase changelog — Breaking Change: Tables not exposed to Data and GraphQL API automatically (published 2026-04-28).
What changes:
- May 30, 2026: new projects stop auto-granting Data API access on new
publictables by default. - October 30, 2026: that same behavior is applied to all existing projects.
- After the change reaches your project, new tables in
publicneed an explicitGRANTbeforesupabase-js/ the Data API can see them. Existing tables keep their current grants and stay reachable.
Grants and RLS are separate. A missing grant fails before any policy runs.
| Error | Meaning | Fix |
|---|---|---|
42501 — permission denied for table | The role has no table privilege (grant missing). | GRANT the needed privileges to anon and/or authenticated. Do not disable RLS. |
| violates row-level security policy | Grant exists, but no policy allows this row/operation. | Add or fix policies. Do not disable RLS. |
Example 42501 shape from PostgREST (from Supabase's changelog):
{
"code": "42501",
"message": "permission denied for table your_table",
"hint": "Grant the required privileges to the current role with: GRANT SELECT ON public.your_table TO anon;"
}Safe pattern for each new table (grant + RLS + policies together):
grant select, insert, update, delete on public.your_table to authenticated; alter table public.your_table enable row level security; create policy "users can read their own rows" on public.your_table for select to authenticated using (auth.uid() = user_id);
Never "fix" either error by turning RLS off. That opens the table. Fix the grant or the policy instead.
Direct Postgres connections (psql, ORMs with a connection string) are not affected by this Data API grant change. Lovable/Bolt apps that use supabase-js are.
5. Never ship service_role to the browser
| Key | Safe in Lovable/Bolt frontend? |
|---|---|
anon / publishable | Yes — meant to be public; protect data with RLS + grants |
service_role / secret | No — bypasses RLS; anyone with it can read and write every table |
If service_role was ever in frontend code, a VITE_ variable, GitHub, or chat history: rotate it in Supabase, put the new secret only in Edge Function secrets or a real server env, and revoke the old key.
6. Test logged out and as a second user
- Open your published app in a private window, logged out. You should not see other people's rows. If you can, RLS is off or a policy is too open.
- Create two test accounts. Sign in as user A, create data, sign out, sign in as user B. User B must not see or edit A's rows.
- In DevTools → Network, inspect Supabase REST calls. Unexpected 200 responses with other users' data mean the policy is wrong.
7. Run Security Advisor
In Supabase: Advisors → Security Advisor.
- Fix every "RLS disabled" / unrestricted-table warning.
- After Oct 30, 2026, also watch for tables that need explicit grants.
- Re-run Advisor after Lovable or Bolt creates a new table.
8. Copy-paste prompts for Lovable / Bolt
Enable RLS on existing tables:
For every public table in my Supabase project that stores user data, enable Row Level Security and add owner-only policies using auth.uid() = user_id (or the correct ownership column). Do not disable RLS. Do not put the service_role key in frontend code. Show me the SQL you will run before applying it.
Create a new table the safe way (post–Oct 30 2026):
Create a public.notes table with id, user_id uuid references auth.users, title, body, and created_at. In the same migration: GRANT select, insert, update, delete on public.notes to authenticated; ENABLE ROW LEVEL SECURITY; add owner-only select/insert/update/delete policies with auth.uid() = user_id. Do not grant write access to anon. Do not use the service_role key in the browser.
Debug 42501 vs RLS errors:
My Supabase call returns either 42501 permission denied for table, or a row-level security policy violation. Do not disable RLS. First check grants for anon/authenticated on that table, then check policies. Show the exact GRANT and CREATE POLICY SQL to fix it for owner-only access.
FAQ
Is the Supabase anon key in my Lovable app a leak?
No. It is designed to be public. The risk is tables without RLS, policies that are too open, or a missing/extra grant.
What is the difference between 42501 and an RLS policy error?
42501 permission denied means the role cannot touch the table at all (grant problem). An RLS violation means the grant exists but no policy allows that row or action. Never fix either by disabling RLS.
Do I need grants after October 30, 2026?
For new tables in public on projects that have received the change, yes — explicit GRANTs are required before the Data API works. Existing tables keep their grants. See Supabase's changelog.
Can I leave RLS off for a "private" table?
No, not if the table is in an exposed schema with grants to anon or authenticated. Without RLS, any client with those grants can read or write the whole table.
Where should service_role live?
Only on the server: Supabase Edge Function secrets, or another trusted backend. Never in Lovable/Bolt frontend code or VITE_ variables.
Want a heads-up when a table is left open?
LeakOwl re-checks your live app for open Supabase tables and leaked keys. Free teaser scan at leakowl.com.
Scan my app free