Safeguard
Security Guides•

PostgreSQL row level security: basics, bypasses and pitfalls

How PostgreSQL row level security works, who bypasses it (owners, superusers, BYPASSRLS), and a short policy example you can adapt.

Safeguard Security Team
3 min read

Row level security (RLS) in PostgreSQL limits which rows a role can read or change, on top of normal GRANT privileges. Enable it with ALTER TABLE ... ENABLE ROW LEVEL SECURITY and add policies. With RLS on and no policy, no rows are visible or modifiable. Table owners, superusers and BYPASSRLS roles skip it.

What it does

Without RLS, a role with SELECT on a table sees every row. With RLS enabled, each row access must be allowed by a policy. Policies can target specific commands (SELECT, INSERT, UPDATE, DELETE, or ALL) and specific roles, and PUBLIC covers everyone.

A short example

CREATE TABLE accounts (manager text, company text, contact_email text);

ALTER TABLE accounts ENABLE ROW LEVEL SECURITY;

CREATE POLICY account_managers ON accounts TO managers
    USING (manager = current_user);

Members of managers can see, update and delete only rows where the manager column equals their own user name. Because the policy has only a USING clause, PostgreSQL applies an identical WITH CHECK clause, so inserts and updates must also satisfy the condition. Other roles see no rows, since no policy grants them any.

Who bypasses RLS

  • Table owners normally bypass row security. ALTER TABLE ... FORCE ROW LEVEL SECURITY makes the owner subject to policies.
  • Superusers and roles with the BYPASSRLS attribute always bypass it.
  • TRUNCATE and REFERENCES are not subject to row security, and referential integrity checks (unique, primary key, foreign key) bypass it too.

The practical lesson: if your application connects as the table owner, your policies do nothing unless you force them. Connect with a separate, non-owner role.

Combining policies

Permissive policies, the default, combine with OR: a row is visible if any policy allows it. Restrictive policies, created with AS RESTRICTIVE, combine with AND. Use restrictive policies for rules that must always hold, such as a tenant boundary, and permissive ones for the ways a role may be allowed in.

Pitfalls to test for

  1. Running the app as the owner or a superuser, which silently skips every policy.
  2. Forgetting that only the table owner can enable RLS and create policies.
  3. Assuming RLS covers every operation. The exemptions above are real.
  4. Testing only with an admin account. Test with the same role the application uses, and with a role that has no policy.

The manual also notes that the predefined roles pg_read_all_data and pg_write_all_data do not bypass RLS, which is helpful for support and analytics access. See predefined roles for least privilege.

Quick check

I enabled RLS but wrote no policy. What do users see?

Nothing. With row security enabled and no policy, no rows are visible or can be modified, except for roles that bypass it.

Does RLS apply to the table owner?

Not by default. Use ALTER TABLE ... FORCE ROW LEVEL SECURITY to subject the owner to policies.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.