RowShield

Guides

What actually goes wrong with Supabase Row Level Security, why it goes wrong more often when an AI wrote the migration, and the SQL that fixes each case.

RLS disabled on a public table: the most common Supabase leak

2 rules

A table in the public schema with Row Level Security switched off returns every row to anyone holding your anon key. Here is the check, the fix, and how to keep it fixed.

The migration that added a table and forgot RLS

1 rule

Generated migrations create tables in seconds and policies never. Find every public table shipped without Row Level Security, with the enable-and-policy SQL ready.

USING (true): the policy that protects nothing

1 rule

RLS enabled, dashboard satisfied, every row public. Constant-true policies are the quietest Supabase leak. Here is how to find and replace them.

WITH CHECK (true): anyone can write anything

1 rule

A write policy with a constant-true WITH CHECK turns your table into a public message board. Find open write paths and close them with scoped CHECK clauses.

INSERT policies without WITH CHECK let anyone write as anyone

1 rule

A missing WITH CHECK is not stricter than a loose one — Postgres falls back in surprising ways. Find INSERT policies that constrain nothing.

RLS enabled, zero policies: silently everything-denied

1 rule

The safe-looking state that quietly breaks your app: RLS switched on, no policies behind it, every non-owner query returns empty. Detect and resolve it.

Policies scoped to service_role are decoration

1 rule

service_role carries BYPASSRLS, so policies targeting it bind nobody. Why the dashboard generates them anyway, and what to write instead.

RESTRICTIVE policies: the modifier people accidentally grant with

1 rule

A RESTRICTIVE policy can only narrow access, never widen it. Misreading it leaves tables looking locked that are wide open — or the reverse.

Without FORCE RLS the table owner bypasses everything

manual check

RLS never binds the table owner unless you force it — and migrations run as the owner. The one-line fix and why generated policies omit it.

auth.uid() returning null in the wrong context

manual check

When auth.uid() evaluates to null — cron jobs, triggers, service calls — equality policies deny rather than error. Diagnose null-context failures.

Two permissive policies are OR’d together — and that surprises people

manual check

Adding a second permissive policy widens access instead of tightening it. How Postgres combines policies, the classic accidental-widening traps, and the audit query.

SECURITY DEFINER functions with an unset search_path

manual check

Definer functions without a pinned search_path can be hijacked by schema objects in the caller-controlled path. The pin pattern and the audit query.

Direct GRANTs to anon that sidestep RLS thinking entirely

manual check

Table and routine GRANTs shape what PostgREST exposes independently of policies. Audit the grant matrix before it contradicts your policy design.

Views that execute with owner rights and skip policy checks

manual check

Pre-15-style views run as their owner, so policies on underlying tables never apply. Find them, convert them, and know what RowShield covers.

The Supabase security gaps Lovable apps ship with

4 rules

Lovable gets a working backend in an afternoon; policies are the step that comes after. The gaps these apps ship with, the requests that reveal them, the fixes.

World-readable tables in a Lovable build — and the one request that proves it

1 rule

A table in a Lovable build returns rows to anyone holding the anon key. Here is the single request that proves it, why dashboards still say protected, and the fix.

Bolt.new builds and the policies that were never written

1 rule

Bolt scaffolds tables fast; CREATE POLICY lines rarely follow. Why enabled-but-policyless tables happen, the quiet deny-all they cause, and the policies to write.

Cursor-written migrations and the RLS line that never appeared

1 rule

Cursor writes the CREATE TABLE and skips the ENABLE ROW LEVEL SECURITY line beneath it. How to review AI-generated migrations for the statements that decide exposure.

Claude Code + Supabase MCP: fast schema, slow policy

2 rules

The Supabase MCP lets Claude Code apply schema changes in seconds while policy work lags behind. What drifts between sessions, the alerts that catch it, how to check.

Replit frontends and everything the anon key can reach

1 rule

A Replit frontend ships the Supabase anon key to every visitor. What that key reaches before policies exist, how to test it yourself, and how to bound it properly.

Taking a v0 + Supabase prototype to production safely

manual check

A v0 prototype with a real Supabase project behind it needs a short list of checks before real users arrive. The checklist, the SQL behind each item, the limits.

Translating Firebase rules thinking into RLS thinking

manual check

Firestore rules and Postgres RLS solve the same problem in different grammar. A translation guide for teams moving from Firebase to Supabase, with worked policies.

Edge functions hold service_role — verify the boundary actually holds

1 rule

Edge functions are the right home for service_role — usually. How the boundary fails in practice, how to verify yours holds, and the rotation order when it does not.

The server/client key split done wrong in Next.js + Supabase

1 rule

One environment variable decides whether Next.js visitors hold an anon key or an RLS-bypassing service_role key. Where the split breaks, and how to prove yours right.

FlutterFlow + Supabase: the RLS half the tutorial skipped

manual check

FlutterFlow tutorials end at the API call; the policy half decides who reads what. The RLS work tutorials omit, with the catalog checks and policy SQL to do it properly.

Internal tools pointed straight at PostgREST

1 rule

Retool and Bubble connect straight to PostgREST, and the anon key decides what they see. Why internal tools leak, the request that tests it, and what finally bounds it.

Anyone can list my users table

1 rule

Your users or profiles table answers strangers with its contents. The one request that proves it, why dashboards still say protected, and the policy that closes it.

My API returns data with no login at all

1 rule

Rows arrive from your REST endpoint with no session and no token beyond the public anon key. Why PostgREST answers, what each response shape means, and the fix.

The dashboard says protected, but the table is still readable

1 rule

RLS reads enabled, indicators look green, yet an unauthenticated request returns rows. The tautological policy behind it, and the queries that expose it.

Your storage bucket is public — so are all of its images

1 rule

Objects in a public bucket download from their URL with no auth check, and paths circulate. What public really serves, how to verify it, and the signed-URL fix.

An open storage.objects policy is a public bucket with extra steps

1 rule

The bucket flag says private, yet objects download anyway. A permissive policy on storage.objects grants what the flag withheld — here is the query and the rewrite.

Queries got slower the day RLS went on

1 rule

Row Level Security evaluates its predicate against every candidate row, and an unindexed column turns each query into a sequential scan. The diagnosis, and one index.

The bill spiked and the only change was policies

2 rules

Compute costs climb after policies land when predicates force sequential scans and bare auth.uid() runs per row. Both habits, the SQL to find them, and the fixes.

The same Security Advisor warning keeps coming back

manual check

You dismissed it, fixed it, watched it clear — and it returned. Why point-in-time checks meet regressing states, and how transition tracking ends the loop.

You fixed it Monday; it was back by Friday

manual check

Fixed Monday, back Friday, nobody touched it. That is a regression — a process restoring yesterday. Where fixes get undone, how to prove it, and what labels it.

What a RowShield health score actually measures

manual check

What feeds the health score, what moves it, and what it deliberately ignores — described qualitatively, with a query to count the raw inputs yourself.

Schema drift in Supabase: what moves, what breaks, what watches

manual check

Tables, policies, buckets and functions move after deploy. A field guide to Supabase schema drift, the failures it causes, and how continuous diffing catches it.

The new column your policy never mentioned

manual check

Adding a column can widen what existing policies expose or break their assumptions entirely. How schema additions interact with RLS, and how to catch the fallout.

Renames that quietly unhook a policy predicate

manual check

Rename user_id to owner_id and every policy referencing user_id stops resolving - erroring loudly or comparing against nothing. Failure modes and the fix loop.

The migration that recreated the table without its policies

2 rules

Drop-and-recreate is the fastest way to change a table and the fastest way to lose every policy on it. Why it happens, the damage pattern, and the guard.

Restores that resurrect last month’s holes

manual check

Point-in-time recovery rewinds data, schema and policies together. Last month’s fixed vulnerability comes back with your data. Plan for it.

Staging and production disagree about who can read

manual check

"It works in staging" assumes the catalogs match. They rarely do - and the differences concentrate exactly where security decisions live.

Policy drift versus the advisor: point-in-time versus continuous

manual check

The dashboard advisor answers "what is wrong right now". Drift detection answers "what changed since last time". Both matter; neither substitutes.

Anatomy of a RowShield drift alert

manual check

What each drift alert carries: the object that moved, the transition type, before-and-after context, severity mapping, and the remediation path when one exists.

Unindexed RLS predicates: the index your policy is begging for

1 rule

An RLS policy filtering on an unindexed column turns every query into a sequential scan. Find the columns yourself, then fix each with one statement.

Wrapping auth.uid(): the InitPlan pattern, explained honestly

1 rule

A bare auth.uid() inside a policy is evaluated once per candidate row. Wrapped in a scalar subquery it runs once per statement, with identical semantics.

RLS patterns that survive millions of rows

manual check

Row Level Security adds work to every query, and the work compounds as tables grow. These are the policy patterns that stay fast past a million rows — reviewed by hand.

Per-row versus per-statement: how policy conditions really run

manual check

Policy conditions are not run once per query. Here is the per-row evaluation model, what an InitPlan changes, and how to read a plan without inventing numbers.

Index strategy for org_id-style multi-tenant policies

manual check

Nearly every Supabase schema filters each table by an org_id or user id. Getting the index shape right is the difference between a seek and a scan on every request.

Thinking like an RLS cost calculator

manual check

You do not need a dashboard to reason about what an unindexed policy costs. A rough model of rows scanned and requests made gets you close, qualitatively and honestly.

A Supabase storage audit you can run in ten minutes

1 rule

Public buckets, open object policies and disabled RLS on storage.objects account for most storage incidents. Walk the checklist, then keep it checked continuously.

Signed URLs or a public bucket: deciding properly

1 rule

Both models are legitimate; they answer different questions. Weigh exposure, expiry, revocation and caching per bucket, then keep whichever you choose monitored.

Exactly what an anon key can do before you write a policy

manual check

The anon key is public by design and harmless while RLS bounds it. Here is what it reaches when policies are missing — table by table, surface by surface.

The two-key mental model, once and for all

manual check

One Supabase key is meant to be public, the other must never leave the server. Anchor the distinction once — including where authenticated users fit — and stop guessing.

Rotating a service_role key that got out: rotate first

1 rule

A service_role key outside your server is an RLS bypass in someone else's hands. Rotate immediately, inventory what breaks, then move the callers server-side for good.

sb_publishable_ keys and the new API-key hierarchy

manual check

Supabase has been moving from JWT-shaped keys to explicit publishable and secret formats. What changes, what stays true, and how to migrate your environment variables.

Row Level Security alongside an external auth provider

manual check

Running Auth0, Clerk or another provider in front of Supabase? Policies still bind everything. Two integration shapes exist, each with familiar, fixable traps.

Realtime authorisation and the policies people forget

manual check

Postgres change streams honour your RLS policies; broadcast and presence never touch the database. Know which half of Realtime each security rule governs.

Supabase security for solo founders: a one-person pass that holds

manual check

A practical security pass for the Supabase project behind your first paying customers: what to lock down this week, and what can wait until revenue lands.

Supabase security across client projects: an agency baseline

manual check

Run Supabase security across many client builds without a platform team: per-project baselines, tiered thresholds and monitoring that survives staff turnover.

From prototype to production: the Supabase checklist

manual check

The pre-launch pass that separates a weekend prototype from a production Supabase backend: RLS forced on every table, keys scoped, buckets deliberate, backups proven.

Health data isolation on Supabase: configuration you can defend

manual check

Isolation requirements for health data on Supabase: relationship-scoped RLS on clinical tables, private document storage, and configuration watching over time.

Student data on Supabase: scoping enrolments, marks and messages

manual check

Enrolment tables, guardian access and pupil records: how education platforms on Supabase scope student data so one class structurally cannot read another.

Your internal tool is reachable by the whole internet

1 rule

Internal tools share the same public URL and anon key as your app. How world-readable ticket tables happen, and how RowShield’s anonymous probe catches them.

Multi-tenancy on Supabase: policy patterns that scale

manual check

Organisation-scoped RLS done properly on Supabase: membership joins, owner overrides, shared-row exceptions, and the conventions that keep a growing schema legible.

Can tenant A read tenant B? A test you can run tonight

1 rule

A repeatable test for whether tenant A can read tenant B: forged JWT claims, SET ROLE, plus the anonymous probe behind RowShield rule ANON_TABLE_READABLE.

Forum databases on Supabase: public by design, private by policy

manual check

Public posts are meant to be public; profiles, private messages and moderation notes are not. A posture pass for community forums running on Supabase.

Chat history is sensitive: securing an AI backend on Supabase

manual check

Conversation history is the most sensitive table in an AI product. Scope it per user on Supabase, keep writes server-side, and fence the embeddings nobody thinks about.

Listings are public; everything around them is not

manual check

Listings are public by design; drafts and seller details are not. Shape RLS for directory and marketplace tables on Supabase so publicity is a decision, not an accident.

An open contact table reads like a customer list, because it is one

manual check

A CRM contact table reachable through the anon key is a breach-shaped problem. Confirm the exposure, scope contacts to workspace members, and put watching in place.

Telemetry writes: RLS on IoT ingest tables

1 rule

Device telemetry arrives from firmware you do not control. Constrain the write path with WITH CHECK so a spoofed device id cannot append into another fleet’s history.

Inheriting a Supabase project: the first-week audit

manual check

Inheriting a Supabase project built by someone else? Map the reachable surface, reconstruct the access model and price the findings before changing anything.

What a SOC 2 auditor asks about your Supabase RLS posture

manual check

What SOC 2 examiners typically ask about database access, and what row-level security evidence looks like. Informational only — not legal or compliance advice.

Access-control posture for GDPR-minded teams on Supabase

manual check

What a defensible access-control posture looks like on a Supabase backend when personal data is in scope. Informational only — this page is not legal advice.

Demonstrating who changed your database policies, and when

manual check

Postgres keeps no native history of policy changes. How to reconstruct one with migrations and logs, and what RowShield records between scans automatically.

Security advisor vs continuous monitoring, drawn precisely

manual check

The dashboard Security Advisor is a good point-in-time check. What continuous monitoring adds is the interval between looks: drift, regressions and behaviour.

Linting policy SQL versus probing the live project

manual check

Static linters read policy definitions before they ship; live probes request the running API after. Different instruments, different failure classes, complementary.

pgTAP-style test harnesses versus managed RLS monitoring

manual check

Test harnesses assert policy behaviour inside CI against a database you point them at. Managed monitoring watches production continuously. They complement.

A fifteen-minute manual Supabase audit — and where it stops

manual check

An ordered checklist for auditing a Supabase project by hand with psql and curl, an honest account of what a snapshot cannot tell you, and how to automate it.

Free ways to audit a Supabase project, honestly compared

manual check

Four free routes to a Supabase security audit — the built-in advisor, hand-run SQL, self-made probes and RowShield's free tier — and what each yields.

A free Supabase RLS audit: paste a URL, read the findings

manual check

Paste your project URL and get a free RLS audit of your live surface plus catalog checks. GET requests only, nothing stored, no account required.

Probe your live app: what its own public key gives away

2 rules

Your deployed app hands every visitor an anon key, sometimes something stronger. Probe your live URLs from outside and read exactly what they expose.

Is my Supabase secure? An honest answer, and how to find yours

manual check

Security reduces to three verifiable things: RLS on every reachable table, policies granting only what you intend, bypass keys off the client.

Checking whether RLS is enabled: one query, read honestly

manual check

One query against pg_class lists every public-schema table with its RLS flag. Here is the query, the FORCE flag people miss, and the fix for a false.

Listing every RLS policy: pg_policies, column by column

manual check

pg_policies renders every policy in your project with its command, roles and conditions. Here is the query, each column explained, and what to hunt for.

Testing RLS as the anon caller, not as the admin

1 rule

Admin sessions bypass the machinery under test. Ask PostgREST as the anon key instead: here is the request, and how to read rows, empty arrays, errors.

Tautological policies: when RLS is switched on and says nothing

1 rule

A tautological policy such as USING (true) admits every row to every role it names while RLS reports enabled. Definition, detection query, honest fix.

Will enabling RLS break my app? What actually changes

manual check

Turning RLS on denies every client row until a policy grants it. Ship enable and policies in one migration and the break window is a single deploy.

The Supabase security checklist, complete and current

manual check

Every check that matters for a Supabase backend across tables, keys, storage, performance and process, each with the query that verifies it.

RLS glossary: every term, in plain language

manual check

RLS, policies, USING and WITH CHECK, tautology, FORCE, BYPASSRLS, service_role, anon, PostgREST, drift, transitions and remediation SQL defined plainly.

Why the same finding returns — and what finally breaks the cycle

manual check

Fixed on Friday, flagged again Monday: findings return because the fix lived in the database while the cause lives in the process. Break both halves.

RowShield for agencies: every client backend, one pane

manual check

Fifteen client projects on one dashboard, alerts routed per destination with thresholds you choose, evidence you can hand each client. Built for rosters.

Adding a policy check to your Supabase CLI workflow today

manual check

Wire rowshield scan into the same flow as supabase db push: one command, three exit codes, remediation SQL printed on failure. Shipped interface, no roadmap.

A GitHub Actions RLS check: the design, and what works today

manual check

What a planned GitHub Actions integration would check and fail on, plus the pattern you can adopt today with plain steps and scheduled scans. No dates promised.

Auditing preview deployments: the plan, and today's practice

manual check

Preview deploys get their own environment variables and their own surprises. What a planned preview-audit integration would do, and how to check previews today.

Slack alerts for RLS changes: contents, thresholds, cadence

manual check

What a RowShield Slack alert contains, how per-destination severity thresholds work, and why alerts fire on transitions only. Slack delivery starts on Indie.

Discord delivery for findings: embeds, thresholds, dedupe

manual check

How RowShield renders findings as Discord embeds, how per-destination thresholds route them, and why repeats stay quiet. Discord delivery starts on the Team plan.

Driving scans from your own tooling: today and planned

manual check

Script scans today through the CLI and its exit codes; a planned API would expose the same semantics over HTTP. Design goals stated, dates promised none.

Policy-as-code habits for Supabase: Terraform and beyond

manual check

Where RLS policies should live, which Terraform habits prevent drift, and how scheduled scans catch the divergence that code review alone cannot.

The migration review habit that stops most Supabase drift

manual check

Most RLS regressions enter through ordinary migrations. A pairing rule, a short reviewer checklist, and scheduled scans as the safety net underneath.

Prevent Supabase RLS bypass

4 rules

Find tables with Row Level Security disabled, policies that evaluate to true, and writes with no WITH CHECK — then keep them from coming back.

Detect a service_role key in your client bundle

1 rule

The service_role key bypasses RLS entirely. Find it in your deployed JavaScript before someone else does — free, no signup, no code access needed.

Why your Supabase bill spikes after adding RLS

2 rules

Unindexed RLS predicates and bare auth.uid() calls turn every query into a sequential scan. Find both, with the SQL to fix them.

Check whether your Lovable app is leaking data

3 rules

Free, no-signup check for tables readable with your public anon key, RLS switched off, and service_role keys in the bundle.

Bolt.new shipped tables without RLS

3 rules

Find every table Bolt created without Row Level Security, and get the enable-plus-policy SQL generated from your real columns.

Audit the Supabase policies Cursor wrote

3 rules

Cursor writes plausible RLS. Check for policies that evaluate to true, writes without WITH CHECK, and predicates with no index.

Detect Supabase schema drift after migrations

4 rules

Know when a table appears without policies, a policy is rewritten, RLS is switched off, or a bucket flips to public — and when a fix regresses.

SECURITY DEFINER views and functions that bypass RLS

manual check

A SECURITY DEFINER view runs as its owner and ignores the caller RLS policies. Here is how to find them yourself, with the catalog queries.

What the Supabase Security Advisor does not do

7 rules

The advisor is a point-in-time check you have to open. Continuous scanning, drift diffing, alerts and CI exit codes are what it is missing.

Continuous RLS monitoring for Supabase

7 rules

Scheduled catalog audits, drift diffing, Slack and webhook alerts on transitions, and CI exit codes — on a paid plan above the free tier.