Prevent PostgreSQL RLS Tenant Leaks with Connection Pools

A PostgreSQL connection pool reuses sessions across requests. A session-level tenant setting can therefore survive after one request releases its connection and affect the next borrower. Set the tenant locally inside a transaction, and run every protected query on that same checked-out client.

Last updated: October 1, 2026.

async function listTenantInvoices(pool, tenantId) {
  const client = await pool.connect();

  try {
    await client.query('BEGIN');
    await client.query(
      "SELECT set_config('app.tenant_id', $1, true)",
      [tenantId]
    );
    const result = await client.query(
      'SELECT id, total FROM invoices ORDER BY id'
    );
    await client.query('COMMIT');
    return result.rows;
  } catch (error) {
    await client.query('ROLLBACK');
    throw error;
  } finally {
    client.release();
  }
}

The third argument to set_config() is true, so the value is transaction-local. The transaction, tenant setting, and protected query all use one client rather than separate calls on the pool.

Make the policy fail closed

A policy can compare tenant_id with current_setting('app.tenant_id', true). The optional true returns null when the setting is absent, so an ordinary equality comparison matches no rows. PostgreSQL documents that set_config(..., true) lasts only for the current transaction.

Enable RLS and test through the same non-owner role used by the application. Superusers, roles with BYPASSRLS, and normally the table owner bypass policies; the row-security documentation explains these exceptions.

Test the pool, not only the policy

Run concurrent tests that alternate tenants through a small pool, omit the tenant context deliberately, and force both commit and rollback paths. Every missing-context query should return no protected rows. Always release the checked-out client in finally; a transaction abandoned on a pooled session is another cross-request hazard.

RLS is defense in depth, not a replacement for application scoping. Pair it with tenant-aware SQL, a deliberate multi-tenant database model, and preserved context in background jobs.

Related Web Cheat Sheet guides

Sergey Kornilov

Sergey Kornilov