
This article is part of our AI-Built Site Rescue hub. Want help applying it? Talk to eSEOspace.
Row-level security (RLS) is a set of database rules that decides which rows each user can read or change. It matters because many modern apps let the browser talk to the database directly.
Without those rules, your app's login screen may be the only thing that looks like protection. This guide explains RLS in plain terms using Supabase, a popular database service for AI-built apps. It is part of our AI-Built Site Rescue hub, and was checked against Supabase's documentation on 9 November 2026.
Key Takeaways
- RLS applies access rules inside the database, so each user only reaches the rows they should.
- Supabase says a table in an exposed schema without RLS is readable and writable by any role with a grant on it.
- Turning RLS on with no policies blocks access through the API with a publishable key until you create policies.
- Policies do not remove existing grants, so older projects may need grants revoked too.
- Views, weak policies and user-editable metadata can each undo the protection you thought you had.
What row-level security does
Imagine a filing cabinet shared by every customer. RLS is the rule that lets each customer open only their own folder.
A database table holds many customers' records together. Supabase describes RLS as "granular authorization rules that run inside the database." Because the rule lives in the database, it applies no matter which page or tool asks for the data.
A rule might say: a signed-in user can only read rows where the user_id column matches their own ID. Each of these rules is called a policy.
Why people keep mentioning it
Many apps built with AI tools use Supabase and talk to it straight from the browser. That is a normal design, and it works well when the rules are correct.
The catch is that a browser app has to carry a key that anyone can read. Supabase's API keys guide says the publishable key is "Safe to expose online," and adds that it "only reaches what Row Level Security allows."
So RLS is doing the real protecting. The issue is unreviewed software in production, not who or what wrote it. A person can forget this step just as easily as an AI tool can.
What happens without it
Supabase's Row Level Security guide is direct. It says "A table in an exposed schema without RLS is readable and writable by any role with a grant on it."
Its Security Advisor describes the same risk in stronger words. It flags any public table with RLS off.
The warning says: "Anyone with your project URL can read, edit, and delete all data in this table because Row-Level Security is not enabled."
An exposed schema is a part of the database that Supabase makes available through its API.
How RLS works, step by step
- You turn RLS on for a table.
- From that moment, Supabase says "no data is accessible through the API when using a publishable key, until you create policies."
- You add policies that open up exactly what each kind of user needs.
- The database checks those policies on every request.
That second step surprises people. An app can appear to break right after RLS is enabled, because nothing is allowed yet. That is the rule working as designed.
Two parts of a policy
Policies use two clauses, which do different jobs.
| Clause | What it controls | Example |
|---|---|---|
USING | Which existing rows an action can see or target | A user can see only their own orders |
WITH CHECK | What a new or changed row is allowed to look like | A user can't reassign an order to someone else |
Supabase's update example uses both clauses. If you leave out WITH CHECK, Supabase says the USING expression decides which rows are visible and which new rows are allowed.
Common mistakes
These are the problems Supabase's own documentation warns about.
Assuming policies are enough
Supabase says: "On projects that still grant anon and authenticated by default, revoke those grants. Adding policies doesn't remove them." A grant is a separate permission that allows a role to use a table at all.
Writing a policy that allows everyone
Supabase's advisor has a warning for policies with always-true conditions, such as USING (true). It flags them because they let the affected roles read or write every row. A policy like that switches RLS on while protecting nothing.
Trusting data users can edit
Supabase says user metadata can be changed by the signed-in user. Its advisor flags policies that rely on it. Store roles and permissions in raw_app_meta_data, which users can't modify, or in your own protected table.
Forgetting about views
A view is a saved query that looks like a table. Supabase says "Views bypass RLS by default because they are usually created with the postgres user."
On Postgres 15 and later, Supabase says to set security_invoker = true so the view follows the table's policies. On older versions, revoke access or keep the view out of an exposed schema.
Ignoring signed-out users
In Supabase, auth.uid() returns the current user's ID. For a signed-out visitor it returns nothing, so a rule like auth.uid() = user_id quietly blocks them.
Supabase recommends stating the check outright: auth.uid() IS NOT NULL AND auth.uid() = user_id.
Putting a secret key in the wrong place
Supabase says "A secret key bypasses RLS only when the request carries no user access token." Secret keys belong on servers you control. Never put one in browser code. If you think one has leaked, follow what to do in the first hour.
How to check your own project
You can do a first pass without writing code.
- In the Supabase dashboard, open Advisors and select the Security Advisor.
- Read any warning about tables without RLS first.
- Look for warnings about always-true policies and user metadata.
- List every table that holds personal data, and write down who should reach each one.
- Test as a signed-out visitor, then as two different signed-in users.
The advisor catches known patterns, but it does not know your business rules. It cannot tell whether a manager should see every customer's orders. A clean report reduces risk, but it does not prove your data is safe.
Make these rules fast, too. Supabase recommends indexing every column your policies filter on, so requests don't slow down as your tables grow.
If your project uses older keys, also read about Supabase's legacy key deprecation. New keys do not change your policies.
Next step
If your app holds customer data and nobody has reviewed these rules, get them checked. Talk to us about a review. It reduces risk, but it cannot certify an app as secure.
Frequently asked questions
What does row-level security mean in simple terms?
It means the database decides who can see or change each row. The rule stays in the database, so it applies wherever a request comes from. Each rule is called a policy.
Is RLS turned on by default in Supabase?
Do not assume it is on. Check each table in the dashboard or the Security Advisor. The advisor specifically warns about public tables where RLS is not enabled.
Is my publishable key safe if it is visible in the browser?
Supabase says publishable keys are safe to expose, but only because RLS limits what they can reach. If your rules are missing or weak, a visible key becomes a real risk. Secret keys are different and must stay private.
Why did my app stop working after I turned RLS on?
Once RLS is on, the API returns no data for a publishable key until you create policies. Your app was probably relying on open access. Add policies for what each user actually needs.
Does RLS replace login and authentication?
No, the two work together. Login tells the database who a person is, and RLS decides what that person can reach. You usually need both.
Is RLS only a Supabase feature?
No. It is a Postgres database feature, and Supabase builds on it. This guide uses Supabase's documentation, since its setup is common in AI-built apps.











