The SSL modes 'prefer', 'require', and 'verify-ca' are treated as aliases for 'verify-full': what this warning means
You deploy a Node app that talks to a hosted Postgres, and somewhere in the logs this shows up:
Warning: SECURITY WARNING: The SSL modes 'prefer', 'require', and 'verify-ca' are treated as aliases for 'verify-full'.
In the next major version (pg-connection-string v3.0.0 and pg v9.0.0), these modes will adopt standard libpq semantics, which have weaker security guarantees.
To prepare for this change:
- If you want the current behavior, explicitly use 'sslmode=verify-full'
- If you want libpq compatibility now, use 'uselibpqcompat=true&sslmode=require'Nothing failed. Queries run fine. But "SECURITY WARNING" in capital letters is not something you scroll past comfortably, and on Vercel the line gets classified as error-level output, which makes it look worse than it is.
The short version
- Your app is not broken and your connection is not insecure. Today, node-postgres treats
sslmode=requireas full certificate verification, which is the strong behavior. - Nothing has changed yet. The warning is about a future major version of the
pgpackage. As of this writing the latest release is pg 8.23.0, and the v9 it warns about has not shipped. - The fix is one word in your connection string: change
sslmode=requiretosslmode=verify-full. Same security you have today, warning gone, and your app keeps that security after the future change lands.
If that is all you needed, you can stop here. The rest of the post explains what the modes actually promise, why the maintainers are changing them, and the edge cases where the one-word fix is not the right one.
You did not upgrade pg. So why now?
The warning lives in pg-connection-string, the small library the pg driver uses to parse connection URLs. It was added in pull request #3473 and first shipped in pg-connection-string 2.10.0 in mid-January 2026.
Here is the part that confuses people: pg depends on that library with a version range, not a pinned version. So you do not need to upgrade pg to start seeing the warning. A fresh install, a lockfile refresh, or a teammate running an update can silently move the transitive dependency to 2.10.0 or later, and an app on pg 8.16.x that was quiet last month starts warning today. That is why "a lot of people" started seeing it around the same time without shipping any database changes.
The warning fires when the connection string is parsed, before any network connection is made, and only once per process. A pool of twenty connections logs it one time, not twenty.
What the SSL modes actually promise
sslmode comes from libpq, the official Postgres C client that psql and most non-JavaScript drivers are built on. In libpq, the modes are a ladder (official reference):
disable: no encryption.prefer: encrypt if the server offers it, otherwise connect anyway. No verification of who you are talking to.require: always encrypt, but do not check the server's certificate at all. The connection is private, but you have no proof it is private with the right party. Think of it as a sealed envelope handed to a stranger.verify-ca: encrypt and check that the certificate was signed by a trusted authority, but do not check that the name on it matches the server you dialed. Any certificate from that authority passes, including one issued for someone else's server.verify-full: encrypt, check the signer, and check the name. This is the only mode that fully protects you from someone impersonating your database server.
So in standard Postgres semantics, require is a much weaker promise than its name suggests.
What node-postgres does today, and what will change
node-postgres never followed the libpq ladder. Today, prefer, require, verify-ca, and verify-full all do the same thing: full certificate and hostname verification, using the certificate authorities your system already trusts. In other words, everything is silently upgraded to verify-full. That is the "treated as aliases" the warning is describing, and it is why the warning is not telling you that you are in danger. You currently have the strongest behavior no matter which of those modes you wrote.
The maintainers plan to align with libpq in the next major version, so that sslmode=require means what it means everywhere else. You can already see the shape of it: uselibpqcompat=true switches on the same parsing branch today. That is reasonable for consistency, but it has a sharp edge. Under libpq semantics, a connection string that says sslmode=require and nothing else drops from "verify everything" to "encrypt only, verify nothing", and one that also passes sslrootcert drops to verify-ca behavior: the certificate chain is checked against that authority, the hostname is not. Managed-provider strings almost never carry sslrootcert, so the first case is the one most apps will land in. Connections that used to be authenticated stop being authenticated, with no error and no visible change. That is exactly the kind of downgrade that deserves a loud warning years in advance, which is what this is.
The fix: sslmode=verify-full
If your Postgres host uses certificates signed by a public authority, which covers Neon, Supabase, Layerbase, and effectively every managed Postgres provider, the fix is to say explicitly what you are already getting:
Neon hands out connection strings in this shape (their docs):
postgresql://user:password@ep-xxx-pooler.us-east-2.aws.neon.tech/neondb?sslmode=require&channel_binding=requireChange one word:
postgresql://user:password@ep-xxx-pooler.us-east-2.aws.neon.tech/neondb?sslmode=verify-full&channel_binding=requireLeave channel_binding alone. node-postgres does support channel binding, but not through that URL parameter: you turn it on in code with the enableChannelBinding: true client option, which makes pg pick SCRAM-SHA-256-PLUS when the server offers it (shipped in pg 8.14.0). The parameter itself is only parsed and carried through by pg-connection-string; pg does not map or enforce it, and wiring it up to libpq's meaning is an open pull request. So it is a harmless passthrough for Node, and other clients that do act on it benefit from it.
Supabase connection strings do not include an sslmode parameter at all, so a Supabase project does not trigger this warning by default. If you are seeing it with Supabase, the parameter was added on your side, and the same one-word change applies.
Layerbase Cloud connection strings do say sslmode=require, so yes, our users on node-postgres see this warning too. If you are connecting from Node, swapping to sslmode=verify-full is safe and free: our server certificates are issued by a public authority, and Node verifies against the trust store it ships with. We hand out require rather than verify-full because our string has to work in every client, and stock psql fails on a bare verify-full (libpq looks for a local root certificate file that most machines do not have, and does not fall back to the operating system trust store the way Node does). We tested that matrix across eight clients and wrote up the results in sslmode across real Postgres clients if you want the receipts.
One trap worth knowing: if sslmode appears in the URL, it takes precedence over an ssl object you pass to the pool programmatically. If you configure TLS in code, remove the parameter from the string entirely.
If you run your own Postgres with a self-signed certificate
The advice above assumes a publicly signed certificate. If you are pointing at your own box with a self-signed or private-authority certificate, note that node-postgres already fails on sslmode=require today (it verifies, remember, so an untrusted certificate is rejected with SELF_SIGNED_CERT_IN_CHAIN). You have two honest options:
- Keep verification and tell Node about your authority:
sslmode=verify-full&sslrootcert=/path/to/your-ca.crt. - Deliberately accept unverified TLS, for example inside a private network you control:
uselibpqcompat=true&sslmode=require.
What uselibpqcompat=true actually does
The second suggestion in the warning text, uselibpqcompat=true&sslmode=require, opts you into the future libpq behavior right now. Under it, require means encrypt but do not verify the certificate, unless you also pass sslrootcert, in which case the chain is verified against that authority and only the hostname check is skipped. It silences the warning because you have explicitly accepted the weaker promise rather than getting it by surprise.
Only reach for it when that weaker promise is genuinely what you want, such as matching psql behavior against a self-signed server. And know that the parameter is a node-postgres invention: it is not part of the Postgres protocol, and non-JavaScript clients will reject a connection string that contains it. Do not put it in a string that anything other than Node will read.
Ways people try to silence it that do not work
node --no-deprecationdoes nothing here. Despite reading like one, this is a plain process warning, not a deprecation warning.node --no-warnings(or the same flag inNODE_OPTIONS) does silence it, along with every other warning your process might ever emit. That is a sledgehammer, and you lose real signals.- There is no dedicated environment variable or option to turn off just this warning.
The connection string change is genuinely the answer. It is also the only response that does something useful: --no-warnings hides the message, while sslmode=verify-full makes your configuration say what it means, today and after v9.
We hand out Postgres connection strings for a living at Layerbase Cloud, which is why we care about the difference between what sslmode says and what your client actually does. If you want a Postgres where TLS is on for every connection string we give you, create one and see for yourself.
Keep reading
- From PGlite to Production PostgresPGlite is a real Postgres compiled to WASM, so graduating a prototype to a hosted database is a dump and a restore, not a rewrite. Here is the whole path, start to finish.
- Your Go app passes in dev and fails in production behind PgBouncerpgx through a transaction-mode pooler fails with prepared statement "stmtcache_1" already exists. Not on the first connection, which is what makes it dangerous. Here is the measurement, the wrong diagnosis we published first, and the server-side fix.
- Which database dumps are byte-identical twice in a rowWe measured every database engine we run to find out whether dumping an unchanged database twice produces the same bytes. Most do not, for a different reason each time, and three cannot be fixed at all.
- We tested sslmode against 8 Postgres clients. Only one value works everywhere.sslmode=require is the only value that connects on every client we tested. verify-full fails on all stock libpq. sslrootcert=system crashes node-postgres before it opens a socket. Here is the measured matrix, with exact versions and the real error strings.