In databases upgraded from PostgreSQL 14 or earlier, every user can create objects in the public schema. PostgreSQL 15 and later defaults support a safer pattern. Run REVOKE CREATE ON SCHEMA public FROM PUBLIC; on older or upgraded databases, then give trusted roles the access they need.
What the public schema is
Every new database contains a schema named public. An object created without a schema name goes there, so CREATE TABLE products means CREATE TABLE public.products. By default everyone has USAGE on public, which means they can look things up in it.
The risk is the CREATE privilege. The manual says that in databases upgraded from PostgreSQL 14 or earlier, everyone has it on public. Anyone with a login can then add objects that other users' queries may pick up.
Why this matters
When a schema is on the search_path, you effectively trust everyone who can create objects in it. The manual states that adding a schema to the path "effectively trusts all users having CREATE privilege on that schema." A user could create a function or table with a name that shadows one another user expects, changing what that user's query does.
The fix
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
The first public is the schema. The second means every user. The manual also advises auditing public for objects that are named like objects in pg_catalog.
Secure patterns the manual describes
- User-private schemas. Make sure no schema has public
CREATE, then give each user a schema with their own name, for exampleCREATE SCHEMA alice AUTHORIZATION alice;. The default search path starts with"$user", so each user lands in their own schema. This is secure unless an untrusted user owns the database or holdsADMIN OPTIONon a relevant role. - Remove
publicfrom the search path, for example withALTER ROLE ALL SET search_path = "$user";, then grantCREATEonpubliconly where needed. The manual warns that calls to functions inpublicbecome unsafe or unreliable under this pattern, so prefer pattern 1 if you keep functions or extensions there.
The manual calls the third option, default path plus CREATE on public for everyone, "never a secure pattern." It is acceptable only with a single user or a few mutually trusting ones.
How to check your own database
Inspect the schema privileges with \dn+ in psql and confirm whether PUBLIC holds CREATE. Check the psql documentation for how to read the privilege column. Do this for every database, because the setting is per database. Then review who owns the database, since that role stays powerful under every pattern. Related reading: least privilege with predefined roles.
Quick check
Does PostgreSQL 15 fix this for databases I upgraded?
Not automatically. The manual says upgraded databases from 14 or earlier keep the open CREATE privilege until you revoke it.
Sources
Last reviewed: 19 October 2026.