Every Postgres connection string accepts an sslmode parameter, and it's easy
to treat it as an on/off switch — encrypted or not. It's actually six distinct levels, each
trading off connection reliability against how strongly you verify who you're actually
talking to.
postgresql://user:password@host:5432/dbname?sslmode=require
The six modes, from weakest to strongest
| Mode | Encrypts? | Verifies server identity? |
|---|---|---|
disable | No | No |
allow | Only if server requires it | No |
prefer | Tries SSL, falls back if unavailable | No |
require | Yes, connection fails otherwise | No |
verify-ca | Yes | Certificate signed by a trusted CA |
verify-full | Yes | Trusted CA, and hostname matches |
disable — no encryption, plain text
The connection is unencrypted. Fine for a database running on localhost
where the "network" is your own machine's loopback interface; risky anywhere traffic could
cross a real network, since credentials and query data travel in the clear.
allow and prefer — opportunistic, not guaranteed
allow tries an unencrypted connection first and upgrades only if the server
insists on SSL. prefer — libpq's actual default — does the reverse: try SSL
first, and silently fall back to plain text if the server doesn't offer it.
The trap with prefer is the word itself. It sounds like "use SSL," but it's
really "use SSL if convenient" — a network intermediary that blocks the SSL negotiation can
force a silent downgrade to an unencrypted connection, and your client won't complain.
require — encrypted, but not verified
require is the first mode that actually guarantees encryption: the connection
fails outright if SSL isn't available. What it still doesn't do is check that the
certificate the server presents is legitimate. A machine impersonating your database with a
self-signed certificate is accepted just as readily as the real server, because
require never checks the certificate chain.
verify-ca and verify-full — actually confirming identity
verify-ca adds a real check: the server's certificate must be signed by a
certificate authority your client trusts. That closes the impersonation gap
require leaves open — but it still doesn't confirm the certificate belongs to
this specific host. A valid certificate for a different server, signed by the same
trusted CA, would still pass.
verify-full closes that last gap by also checking that the certificate's
hostname matches the host you're connecting to — the same check your browser does for HTTPS.
It's the strongest mode, and the right default for any connection crossing a network you
don't fully control.
Picking one
- Local development, same machine:
disableis reasonable — there's no network to intercept. - Managed cloud Postgres (RDS, Supabase, Neon, etc.): most providers
default to requiring SSL already; use at least
require, andverify-fullif the provider gives you a CA certificate to pin against. - Self-hosted, over the public internet:
verify-full, with your own CA certificate configured. Anything weaker leaves you open to a man-in-the-middle presenting a fake certificate.
QuerySQL's Postgres connection form exposes disable,
prefer, and require directly, along with an option to trust
self-signed certificates for internal or development servers where a full CA chain isn't
set up. See the FAQ for the rest of how Postgres connections are
handled.