RowShield
Guides

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

Forums present an unusual brief because much of the database is supposed to be public: posts, threads, reaction counts. The craft lies at the perimeter of that publicness — member profiles revealing too much, moderation notes naming reporters, private messages, draft replies — and in keeping the public bulk cheap to serve without weakening the private core.

Supabase suits the shape well: PostgREST serving public reads happily, RLS fencing everything else. The recurring failure is gradient blindness — one policy style applied everywhere, so the moderation queue inherits the openness of the front page. A posture pass exists to restore the gradients deliberately.

RowShield does not detect this yet. This guide gives you the catalog queries to check it yourself. The nine rules that do ship are listed on the rules index.

Sort the schema by intended audience

Walk every table and label it: fully public, member-visible, participant-visible, staff-only. Posts land public; profiles land member-visible with an agreed column set; messages and reports land participant or staff. The labelling exercise sounds trivial and routinely surprises — the thread-votes table nobody labelled, for instance, turns out to carry voter identities.

Views inherit this audit too. A convenient profile_summary view assembled for the frontend may expose columns the underlying table carefully excludes, because PostgREST serves views under their own grants. Fence views with the same seriousness as tables, or rebuild them over a narrowed basis so the public face stays chosen.

Publishing as a state, not a default

Give posts an explicit lifecycle — draft, published, removed — and let the public policy read only the published arm, while authors read their own drafts and moderators read all. Three narrow grants outperform one generous condition, and the removed state provides a takedown path that avoids destroying history moderators may still need.

ALTER TABLE public.posts ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.posts FORCE ROW LEVEL SECURITY;

CREATE POLICY "anyone_reads_published"
  ON public.posts
  FOR SELECT
  TO anon, authenticated
  USING (status = 'published');

CREATE POLICY "authors_read_own_posts"
  ON public.posts
  FOR SELECT
  TO authenticated
  USING (author_id = (SELECT auth.uid()));

Profiles, queues and private lanes

Profiles need narrowing, not hiding. Members expect display names and join dates; they do not need email addresses, birth years or the internal notes a moderator recorded. Publish a deliberate column set — often through a view — so the public face of a member is a decision rather than a schema accident discovered by strangers.

Moderation queues concentrate the sharpest data: reported content, reporter identities, internal reasoning. Scope them to a staff role maintained in a table, never to a claim anyone might forge, and treat reporter anonymity as a column-level concern — the queue may need the report without the reporter’s name attached.

Private messages, if the forum has them, want participant-only policies derived from a membership table of the thread, plus a considered stance on whether staff may ever read them. Deciding that once, in policy text, beats discovering the de facto answer during an incident.

Keeping the gradients honest

Gradients blur over time: a plugin adds a table, a theme requests a wider profile view, an export feature reads more than it shows. Schedule a periodic re-labelling against the four audiences, and treat any table whose label is disputed as staff-only until argued otherwise. Disputes resolve downwards safely.

Between reviews, RowShield watches the structural floor: tables with RLS disabled, tables answering the anonymous probe, buckets gone public. Scans read catalogue and bucket metadata only — never post bodies or messages — alert on transitions rather than nagging on schedule, and come from an independent product unaffiliated with Supabase. RowShield does not automate the labelling walkthrough itself; that remains a person’s task.

Frequently asked

Should anonymous visitors read posts at all?
Usually yes — reachability drives discussion and search indexing drives growth. The discipline is that anon access is granted explicitly to published content only, never achieved by leaving a table unguarded, which accidentally publishes drafts and removals alongside the front page.
How do we handle removed posts?
Prefer a removed status over deletion: the public policy stops serving the row, staff retain the audit trail, and author histories stay coherent. Hard deletes belong to legal obligations, not routine moderation, because they destroy the evidence trail moderators rely upon.
Are forum databases really targeted?
They are scraped constantly, usually for content rather than intrusion. Risk concentrates where gradients blur — member tables carrying contact details, moderation tables carrying identities. Keeping those lanes fenced is what turns scraping from a violation of members into merely an annoyance.

Check your project in about ten seconds

Paste a URL. No signup, no writes, nothing stored.

Run the free audit
forum supabase securitycommunity platform rlspublic posts private messages postgresforum moderation data protection