惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
M
MIT News - Artificial intelligence
D
Docker
S
SegmentFault 最新的问题
B
Blog
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
博客园 - 【当耐特】
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志
U
Unit 42
C
Check Point Blog
GbyAI
GbyAI
美团技术团队
Recent Announcements
Recent Announcements
F
Fortinet All Blogs
量子位
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
G
Google Developers Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
PostgreSQL Row Level Security: A Complete Guide
Yasser B. · 2026-04-23 · via DEV Community

Yasser B.

Your application code knows which tenant owns which row. Your ORM always filters by WHERE tenant_id = $1. Your team has reviewed the queries and they look fine.

Then someone forgets the WHERE clause. Or a bulk operation skips the filter. Or a new developer writes a raw query without knowing the convention. Suddenly one tenant can read another tenant's data, and you find out from a support ticket two weeks later.

Row Level Security (RLS) moves the tenant isolation logic inside PostgreSQL itself. The database enforces the policy automatically on every access, regardless of how the query was written.

What Row Level Security Does

Enable RLS on a table:

ALTER TABLE documents ENABLE ROW LEVEL SECURITY;

Enter fullscreen mode Exit fullscreen mode

Without any policies, no rows are visible to non-superusers. The safe default is deny, not permit. Then create a policy:

CREATE POLICY documents_tenant_isolation
  ON documents FOR ALL
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

Enter fullscreen mode Exit fullscreen mode

Setting the Tenant Context

Always use SET LOCAL (not SET) with connection poolers. SET LOCAL resets when the transaction ends, so pooled connections do not carry the wrong tenant context into the next request:

BEGIN;
SET LOCAL app.tenant_id = '550e8400-e29b-41d4-a716-446655440000';
-- your queries here
COMMIT;

Enter fullscreen mode Exit fullscreen mode

FORCE ROW LEVEL SECURITY

Table owners bypass RLS by default. Close this gap:

ALTER TABLE documents FORCE ROW LEVEL SECURITY;

Enter fullscreen mode Exit fullscreen mode

Without it, an application connecting as the table owner silently ignores all policies. This is the most common RLS gotcha.

Permissive vs Restrictive Policies

Multiple policies on the same operation combine with OR by default (permissive). For rules that must always apply, use AS RESTRICTIVE. Restrictive policies combine with AND against all other policies.

Performance

Add an index on the tenant_id column:

CREATE INDEX idx_documents_tenant_id ON documents (tenant_id);

Enter fullscreen mode Exit fullscreen mode

Without it, every query with an RLS filter becomes a full table scan.

Common Mistakes

  • Not using FORCE ROW LEVEL SECURITY when the app connects as the table owner
  • Using SET instead of SET LOCAL with PgBouncer in transaction mode (tenant context leaks between clients)
  • Missing the index on the tenant_id column
  • Not testing cross-tenant access explicitly in your test suite

For the full guide with multi-tenant schema setup, testing patterns, EXPLAIN output, and inspecting existing policies, read the full post at rivestack.io.


Originally published at rivestack.io