AI-coded apps

    Supabase RLS Disabled Warning: How to Fix Promptly

    Supabase dashboard showing an RLS disabled warning on a public-schema table that is open to the public anon key until Row Level Security is enabled.

    The Supabase RLS disabled warning means a table in your public schema has Row Level Security turned off, which makes it fully readable, writable, and deletable by anyone using your public anon key, so it is a genuine exposure to fix promptly, not a cosmetic notice. The fix is to enable Row Level Security on the flagged table and then add policies, because enabling it denies all access by default until you write policies that grant the specific access your app needs. Do this for every table the warning flags. If the table held sensitive data while the warning was showing, treat it as possibly accessed and review your data, since a public table with Row Level Security off has no barrier between the public key and its contents.

    Short answer

    The warning flags a public-schema table with Row Level Security off, which is open to your anon key, so enable Row Level Security and add policies. Per Supabase's Row Level Security docs, a table without Row Level Security is fully accessible to the public anon key, and enabling it denies all access by default until you add policies. Per Supabase's API keys docs, the anon key is public and ships in your app, so nothing but your policies stands between it and the data. Enable Row Level Security on each flagged table, add policies tied to the signed-in user, and confirm the anon key cannot reach data it should not.

    What the RLS disabled warning means

    The warning, often shown as RLS disabled in a public table, is Supabase telling you that a specific table is exposed because Row Level Security is off on it. Supabase surfaces this in the dashboard because a table in the public schema without Row Level Security is reachable directly by the public anon key that your app carries, which means anyone can query it. The warning is a real security alert about that table, not a suggestion you can dismiss, because the consequence is an open table.

    Concretely, while the warning shows, that table can be read, modified, and deleted by anyone who has your anon key, which is public and embedded in your app, so effectively anyone. The warning appears per table, so you may see it on some tables and not others depending on which have Row Level Security enabled. Treat each instance as a table to fix, and understand that the app functioning normally does not mean the table is safe, since a table with Row Level Security off works for your app while being open to everyone.

    What is Row Level Security?

    Row Level Security is the Postgres feature, used by Supabase, that controls access to a table on a per-row basis according to policies you write. With it enabled on a table, every request is evaluated against those policies, which decide what that request may read or write, so a user can be limited to only their own rows, or to nothing, based on rules you define. It is the mechanism that makes the public anon key safe, because it constrains what that key can reach.

    In the Supabase model this matters because your app talks to the database directly with the public anon key, rather than through a private server that mediates access. That design is secure only because Row Level Security is expected to enforce access at the database. So Row Level Security is not an optional hardening step for a public-schema table; it is the access control, and a table without it has no access control at all against the public key. The warning exists precisely because a public table with Row Level Security off has removed that control.

    Default policies: what happens when you enable it

    A common question is what policies you get by default when you enable Row Level Security, and the answer is none, which is deliberate and important. Enabling Row Level Security on a table switches it to deny by default: with the feature on and no policies written, the table permits no access at all through the anon key. So enabling it immediately closes the exposure the warning is about, but it also means your app cannot read or write that table until you add policies granting the access it needs.

    This deny-by-default behavior is why the fix has two parts, not one. First you enable Row Level Security, which stops the open access. Then you add policies that grant the specific access your app requires, typically allowing users to read and write only their own rows by comparing an owner column to auth.uid(), the signed-in user's identifier. There is no default policy that magically fits your app, so you write the starter policies for the operations you intend to allow, and grant nothing you do not. Enabling without adding policies leaves a safe but non-functional table; adding the right policies makes it both safe and usable.

    How to fix the warning promptly

    Fixing the warning is quick, which is why leaving it is never worth it. In the Supabase dashboard, open the flagged table and enable Row Level Security on it, which immediately closes the open access and clears the warning for that table. Because the table now denies all access by default, add the policies your app needs next, writing them to grant only the access you intend, scoped to the authenticated user where the data is per-user.

    Then verify and repeat. Test using the anon key that you can reach the data you should and cannot reach data you should not, ideally as different users, to confirm the policies behave. Do this for every table the warning flags, since the alert is per table and one table left open is still an exposure. Avoid the shortcut of writing a policy that grants everyone access, such as a condition that is always true, because that technically enables Row Level Security while leaving the table open, defeating the point. Enabling Row Level Security with correct, user-scoped policies is the actual fix.

    Which keys are safe

    Because this is about the anon key reaching an open table, it helps to be clear on which keys are safe where. The anon, or publishable, key is public and belongs in your app, and it is safe precisely when Row Level Security constrains it, which is what the warning is about restoring. Shipping the anon key is expected; the protection lives in your policies, not in hiding the key.

    The service_role, or secret, key is the opposite and must never appear in your app, because it bypasses Row Level Security entirely and has full access, so no policy would protect you if it leaked. If a service_role key has ever been in your client, rotate it immediately in the dashboard and move its operations to a server, since that is a full compromise, unlike the public anon key. So enable Row Level Security to make the anon key safe, and keep the service_role key strictly server-side.

    If data was exposed: contain it

    If the flagged table held sensitive data while Row Level Security was off, treat it as potentially accessed, because a public table with the feature disabled has no barrier and no reliable way to know it was not read. Enable Row Level Security and add policies first to stop further access, then review your database and any logs for unexpected reads, writes, or deletions, and restore from a backup if data was altered or removed.

    While containing it, check the rest of your setup, since an open table often accompanies other shortcuts. Confirm no service_role key ever shipped in your app, and rotate it if there is any chance it did. The point is that enabling Row Level Security stops the ongoing exposure, and reviewing your data and keys ensures the incident is fully addressed rather than merely closed going forward.

    Warning states compared

    Comparing table states clarifies what clears the warning and what only appears to. The table below sets them out.

    Table stateWarning shownAnon key access
    Row Level Security off, public schemaYes, the disabled warningFull read, write, and delete
    Row Level Security on, no policiesNoDenied by default, safe but unusable
    Row Level Security on, user-scoped policiesNoOnly the rows policies allow
    Row Level Security on, an always-true policyNo warning, but openEveryone, a misconfiguration

    Read the table by both columns: only the third row is both warning-free and actually secure, since the last row clears the warning while leaving the table open.

    Fix checklist

    Working through these steps clears the warning correctly. The checklist below covers them.

    StepActionDone?
    Enable Row Level SecurityOn the flagged table[ ]
    Confirm deny by defaultNo access until policies are added[ ]
    Add starter policiesScope access to the signed-in user with auth.uid()[ ]
    Avoid always-true policiesDo not grant everyone access[ ]
    Test with the anon keyCannot reach data it should not[ ]
    Repeat per tableFix every table the warning flags[ ]

    The step people get wrong is adding a policy that grants everyone access to make the app work, which reopens the table, so scope policies to the user instead.

    Scan your app for a shipped secret

    Enabling Row Level Security fixes the database side, but a related exposure lives in your app: a service_role key bundled into the client bypasses every policy you just wrote, so confirming what keys ship is worth doing alongside the fix.

    A scanner like PTKD.com analyzes your app build and reports issues such as leaked keys and secrets, insecure data storage, and over-broad permissions by severity, mapped to OWASP MASVS, so a service_role key that reached the client is flagged, while the anon key is expected. To be clear about the boundary: PTKD does not enable Row Level Security or write your policies, which you do in Supabase. It confirms your shipped app does not carry a secret that would defeat the Row Level Security you just enabled.

    What to take away

    • The RLS disabled warning means a public-schema table has Row Level Security off, so the public anon key can read, write, and delete all its rows, which is a real exposure to fix promptly.
    • Row Level Security is the per-row access control that makes the public anon key safe, so a public table without it has no access control at all.
    • Enabling Row Level Security denies all access by default, with no automatic policies, so you must add starter policies scoped to the signed-in user for your app to work.
    • Fix every flagged table, avoid always-true policies that reopen the table, and test with the anon key that private data is blocked.
    • Keep the service_role key server-side, and scan your app with a tool like PTKD.com to ensure no secret defeats your new policies.
    • #supabase
    • #row level security
    • #rls warning
    • #database security
    • #anon key

    Frequently asked questions

    What does the Supabase RLS disabled warning mean?
    It means a specific table in your public schema has Row Level Security off, so it is reachable directly by the public anon key your app carries, and anyone can read, modify, or delete its rows. Supabase shows it as a security alert per table, not a dismissible suggestion, because the consequence is an open table. The app working normally does not mean the table is safe, since a table with Row Level Security off functions for your app while being open to everyone.
    What is Row Level Security in Supabase?
    It is the Postgres feature Supabase uses to control access to a table per row according to policies you write, so with it enabled every request is evaluated against those policies to decide what it may read or write. It is the mechanism that makes the public anon key safe, because your app talks to the database directly with that key and Row Level Security enforces access at the database. A public table without it has no access control against the public key.
    What default policies do I get when I enable RLS?
    None, deliberately. Enabling Row Level Security switches the table to deny by default: with the feature on and no policies written, the table permits no access through the anon key, which closes the exposure but also means your app cannot read or write it until you add policies. There is no automatic policy that fits your app, so you write starter policies granting the access you intend, typically scoping rows to the signed-in user with auth.uid().
    How do I fix the RLS disabled warning promptly?
    In the Supabase dashboard, open the flagged table and enable Row Level Security, which immediately closes the open access and clears the warning. Because the table now denies all access by default, add policies granting only the access your app needs, scoped to the authenticated user. Test with the anon key that you can reach data you should and cannot reach data you should not, and repeat for every table the warning flags.
    Can I just add a policy that allows everyone?
    No, that defeats the purpose. A policy whose condition is always true technically enables Row Level Security and clears the warning while leaving the table open to everyone, which is a misconfiguration, not a fix. Write policies scoped to the authenticated user, allowing users to reach only their own rows, and grant nothing you do not intend. The goal is a table that is both warning-free and actually secure, not just one that silences the alert.
    Which Supabase keys are safe with the anon key open table?
    The anon, or publishable, key is public and belongs in your app, safe precisely when Row Level Security constrains it, which is what the warning is about restoring. The service_role, or secret, key must never appear in your app, because it bypasses Row Level Security entirely, so no policy protects you if it leaks. If it ever shipped in your client, rotate it immediately and move its operations to a server. Scan your app with PTKD.com (https://ptkd.com) to confirm only the anon key ships.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free