A shared archive should not mean shared private notes
A hypothetical museum has an online exhibit catalog and a volunteer workspace. Public labels describe the artifacts, but internal notes may mention private donors, condition concerns, or unapproved attributions. Filtering the browser display is not sufficient: a user could still query an exposed database endpoint for rows that the UI never renders.
Supabase Row Level Security, or RLS, applies access rules inside Postgres. Treat it as a database authorization boundary alongside carefully chosen grants and server-side procedures. This guide uses a fictional archive-notes table and describes a least-privilege design, not a live penetration test.
Separate public descriptions from restricted annotations
Model public catalog facts and private work notes as separate tables or carefully separated resources. In a private notes table, keep a stable record ID, collection ID, author or owner reference, note status, and minimal content. Avoid duplicating sensitive donor names across a wide array of publicly exposed tables or search indexes.
Decide the access matrix before writing SQL: a signed-out visitor sees no restricted rows; an authenticated volunteer sees only notes assigned through approved membership; and an authorized conservator can access the subset required by their work. “Can open the catalog page” should never imply “can read all annotations.”
Grants and row policies are two separate gates
Supabase’s current documentation emphasizes that Postgres first checks privileges and then applies RLS policies. Enabling RLS alone does not revoke existing grants. Review SELECT, INSERT, UPDATE, and DELETE access for the anon and authenticated roles and remove operations that the application does not need.
For a read-only volunteer view, grant SELECT only where appropriate, then write a SELECT policy that checks an approved collection membership or owner relationship. Do not keep write privileges merely because a development prototype granted them by default. A blocked query may reflect a missing grant before the row policy even runs.

Enable RLS on exposed data tables
Enable row level security for every private table exposed through the Data API. A policy can use auth.uid() to compare the current signed-in user to an owner or membership record. When there is no authenticated user, auth.uid() returns null, so tests should explicitly include signed-out and expired-session requests.
Keep policies narrow and readable. Different operations need different checks, with USING controlling eligible existing rows and WITH CHECK controlling new row values on inserts or updates. A write policy that only checks what an editor can see can accidentally let them assign ownership to an unauthorized account.
Test collection membership rather than trusting a UI role
If volunteers work on different collections, membership should be represented in an authorized table or trusted claim that the policy can evaluate. Test a member accessing their collection, a different member accessing the wrong collection, and an account whose access has been revoked. A visible badge in the browser is not enough to establish the database identity.
Be careful with recursive policy dependencies and helper functions. Supabase documents security-definer patterns for trusted membership checks, but those functions can run with their creator’s privileges. Keep privileged helpers out of exposed schemas, pin their search_path, and grant execute permissions only as needed.
Illustrate how exposure changes at each gate
In the first state, an over-permissive exposed table can return rows to an unintended caller. In the second, table grants and RLS restrict which operations and rows are eligible. In the final state, a signed-in member receives only the notes that match an approved collection assignment, while a non-member receives none.
The depicted rows are invented examples with deliberately generic labels. A green scoped result is an authorization test outcome, not proof that the note is accurate, safe to publish, or cleared by a curator. Access control and editorial approval must remain separate.

Do not confuse the browser key with an admin key
Supabase distinguishes public client access from privileged server credentials. The service_role bypasses RLS, so secrets granting that role must stay in trusted server infrastructure and out of browsers, URLs, and telemetry. A server job that can bypass RLS should implement its own authorization and should not accept an arbitrary user-supplied collection ID as sufficient approval.
Review any background AI retrieval or summarization worker as if it were a new application user. If the worker queries through a service credential, RLS might not protect private notes at all. Scope the worker to approved records before retrieving data and keep any summaries from leaking information across collections.
Remember that views can bypass policies by default
A convenient SQL view over private notes is not automatically safe. Supabase documents that views often execute with the privileges of their creator and can bypass underlying RLS unless configured appropriately. On supported Postgres versions, a security_invoker view can make the caller’s access rules apply to its referenced tables.
For older setups or unsuitable views, revoke access or move them to an unexposed schema. Test the actual Data API route used by the client; checking table policies in isolation can miss an accidentally exposed view or privileged RPC function.
Build an explicit allow-and-deny test matrix
Prepare tests for anon, member A, member B, revoked member, curator, and trusted backend contexts. For each actor, check SELECT, INSERT, UPDATE, and DELETE where applicable. Assert both that approved reads work and that unauthorized writes or cross-collection reads fail. Tests should be run against a controlled test database with synthetic notes.
Supabase recommends database policy tests and documents its test tooling. Run those tests after schema migrations, policy changes, or a new public API route is introduced. Treat a returned empty result carefully: it might be correct policy filtering, a broken membership join, or a caller without the expected auth token.

Release the catalog only after the access audit
Check every exposed table and view, relevant grants, RLS policies, function privileges, service credentials, and application routes. Document the data classification for each note field and who may approve publication. Strip private donor details from monitoring systems and sample datasets before sharing access logs.
This article is a documentation-based security design exercise, not a certification of a deployed Supabase app. Recheck current Supabase project defaults, Auth claims, and Postgres version before applying policy SQL, then have a qualified owner review the exact authorization model for your museum.
How We Research
For this November 13, 2026 desk review, we consulted the vendor documentation: Supabase Row Level Security guide; Supabase securing your API guide. We compared documented request fields, response rules, edge cases, identity boundaries, and failure paths against the fictional institutional workflow, then designed original conceptual graphics. We did not run requests against vendor accounts, inspect real users or records, measure security outcomes, or certify a deployed system. Check your current product configuration, API version, access rights, platform documentation, and organization policy before implementing these steps in production.
Frequently Asked Questions
No. Table grants and row policies are different checks. Review or revoke excess privileges in addition to enabling RLS and adding policies.
The outcome depends on table grants and policies. A private notes table should be tested to ensure signed-out requests cannot read restricted rows.
No. Privileged service-role access bypasses RLS, so keep those credentials server-side and enforce authorization in privileged workflows.
Views can execute with their owner’s privileges. Review security_invoker behavior or keep restricted views in unexposed schemas.
Test anonymous users, assigned volunteers, unassigned volunteers, revoked members, and privileged services for every relevant read and write operation.
