Skip to content

Matches page titles and page text. Forty pages, indexed at build time.

TLS and certificate verification

What your endpoint presents, which trust store each client checks it against, why verify-full is the only acceptable mode, and what renewal asks of you.

Database connections are TLS-only. There is no plaintext port and no downgrade path.

There is no bundle to download

Your endpoint presents a publicly trusted certificate. Your client verifies it against the trust store it already has, so there is no file to fetch, no path to deploy and nothing to redeploy when a certificate is replaced.

This page said the opposite until 22 September 2026. The earlier design issued from a certificate authority of ours. That meant every client needed a copy of our roots before it could verify anything, and no such copy was ever downloadable. Public issuance removes the step rather than improving it.

Which store your client reads

Three stores, four clients. One connection string for all of them is right for libpq and for pgx, and wrong for the other two.

ClientWhat you setStore
psql and anything on libpqsslmode=verify-full sslrootcert=systemThe operating system's
Go, pgxThe same two keywordsThe operating system's
Java, pgjdbcsslmode=verify-full&sslfactory=org.postgresql.ssl.DefaultJavaSSLFactoryThe JVM's cacerts
Node, pgssl: { rejectUnauthorized: true, servername: HOST }Node's own roots, or the operating system's

sslrootcert=system needs libpq 16 or newer; on an older libpq give it the path to the operating system's bundle instead: /etc/ssl/certs/ca-certificates.crt on Debian and Ubuntu, /etc/pki/tls/certs/ca-bundle.crt on RHEL and its family, and $(brew --prefix)/etc/openssl@3/cert.pem with Homebrew's libpq on macOS.

Java is the one that fails in a confusing way. pgjdbc's default SSL factory imitates libpq: it reads a root certificate from ~/.postgresql/root.crt and throws a FileNotFoundException naming that path when nothing is there. sslmode=verify-full on its own is not enough for this driver.

Node is the one that fails silently. rejectUnauthorized: true verifies the chain and stops there; servername is what makes it check the hostname. Without it you have verify-ca under another name. NODE_EXTRA_CA_CERTS is how you add a root to whichever list your build reads.

Client examples has each of these as a working snippet.

Use verify-full, and nothing weaker

ModeWhat it checksUse it
disableNothing. No encryptionNo
requireEncryption only. Any certificate is acceptedNo
verify-caThe chain, but not the hostnameNo
verify-fullThe chain and that the hostname matchesYes

require is the mode that catches people out. It encrypts the connection and accepts a certificate from anybody, which means it protects against passive interception and not against an active one.

Connect to the hostname, never to an IP address. verify-full checks the hostname against the certificate, and an IP address will not match it.

Renewal

Certificates are short-lived and are replaced before they expire. Nothing on your side changes when one is: your client is checking a public trust store, not a file we handed you, and that store is maintained by your operating system, your JVM or your Node build rather than by us.

That is the whole of what this design asks of you, and it is the reason it replaced the previous one.