Skip to content

The SSL modes 'prefer', 'require', and 'verify-ca' are treated as aliases for 'verify-full': what this warning means

10 min readPostgreSQLDeveloper ToolsSecurityDatabases

Short version: nothing is broken and nothing has changed yet. node-postgres currently treats sslmode=prefer, require, and verify-ca as verify-full, which means it already checks both the certificate chain and the hostname, so the warning is telling you about a future downgrade rather than a present weakness. It appeared without you touching anything because pg depends on pg-connection-string through a version range, so a fresh install or a lockfile refresh can pull in 2.10.0 or later on its own. The fix is one word in your connection string: change sslmode=require to sslmode=verify-full. That is the security you already have, stated explicitly, and it is the security you keep after pg 9 lands.

You deploy a Node app that talks to a hosted Postgres, and somewhere in the logs this shows up:

text
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 timing matters here too: as of this writing the latest release is pg 8.23.0, and the v9 the warning refers to has not shipped. Nothing changes under you until it does, and by then your connection string already says what you meant.

If the one-word fix 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 that 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):

text
postgresql://user:password@ep-xxx-pooler.us-east-2.aws.neon.tech/neondb?sslmode=require&channel_binding=require

Change one word:

text
postgresql://user:password@ep-xxx-pooler.us-east-2.aws.neon.tech/neondb?sslmode=verify-full&channel_binding=require

Leave 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-deprecation does nothing here. Despite reading like one, this is a plain process warning, not a deprecation warning.
  • node --no-warnings (or the same flag in NODE_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.

FAQ

What does "The SSL modes 'prefer', 'require', and 'verify-ca' are treated as aliases for 'verify-full'" actually mean?

It is a statement about what node-postgres does today, not a report of a problem. All four of those modes currently produce full certificate and hostname verification against the trust store Node ships with. The warning exists because that will stop being true in pg-connection-string 3.0.0 and pg 9.0.0, when the modes take on their real libpq meanings and require drops to encryption with no verification at all.

Is my connection insecure right now?

No. If you are on a current pg today, sslmode=require is doing what verify-full does, so both the signer and the hostname are checked. The risk is entirely in the future: the same string, unchanged, will authenticate less once v9 ships. That is why the warning arrived years before the change rather than alongside it.

Why did this start appearing when I never upgraded pg?

Because the warning lives in pg-connection-string, which pg depends on with a version range rather than a pinned version. A fresh install, a lockfile refresh, or a teammate running an update can move that transitive dependency to 2.10.0 or later while pg itself sits at the same 8.16.x you have had for months. Nothing about your database or your code changed.

Can I turn the warning off instead of editing the string?

Not selectively. node --no-deprecation has no effect because this is a plain process warning, and node --no-warnings silences it along with every other warning your process will ever emit. There is no environment variable or option scoped to this one message, which is deliberate: the warning is asking you to make a decision, and hiding it does not make the decision.

Should I put sslmode=verify-full in the connection string I paste into psql?

No, and this is the trap. Stock libpq looks for ~/.postgresql/root.crt rather than the operating system trust store, so a bare verify-full fails on psql and psycopg even against a perfectly valid public certificate. verify-full is the right value for a Node client where you control the consumer; for a string a human will paste into psql or a GUI, require is still the only value that works everywhere. We measured the whole matrix in sslmode across real Postgres clients.


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.