Home / Blog / Postgres SSL Modes

postgres connections

Postgres SSL Modes Explained

Six values, six different guarantees — and the default is weaker than most people assume.

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

ModeEncrypts?Verifies server identity?
disableNoNo
allowOnly if server requires itNo
preferTries SSL, falls back if unavailableNo
requireYes, connection fails otherwiseNo
verify-caYesCertificate signed by a trusted CA
verify-fullYesTrusted 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: disable is 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, and verify-full if 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.

← SQLite rowid vs. Primary Key Back to blog →