SSL Verification Error: Why the Certificate Can’t Be Trusted and How to Fix It

If an updater, a `git` pull, a `curl` request, a package manager, or an application has ever failed with a blunt `SSL verification error` — or its close cousins `certificate verify failed`, `unable to get local issuer certificate`, or `self-signed certificate in certificate chain` — you have hit a very specific class of problem. The connection itself worked. The server presented a certificate. Your client simply could not trust it.

This is a different failure from one where the connection never opens or the handshake collapses. A verification error means the TLS channel got far enough to receive a certificate, and then your client looked at that certificate and said: *I cannot confirm this is genuine.* This guide explains exactly what certificate verification checks, the common reasons it fails, and how to fix the root cause — not bypass it.

Key Takeaways
• An SSL verification error means the certificate was received but could not be trusted — it fails *after* the connection succeeds, during validation.
• The single most common cause on servers, containers, and apps is a missing or outdated CA certificate bundle (`ca-certificates`).
• Other frequent causes: an incomplete certificate chain (missing intermediate) served by the server, an untrusted or self-signed CA, an expired certificate, a hostname mismatch, or a wrong system clock.
• Diagnose with `openssl s_client -connect host:443` (read the `Verify return code`) and `curl -v`.
Do not “fix” it by disabling verification (`curl -k`, `sslVerify false`). That removes the security, not the problem — and exposes you to man-in-the-middle attacks.

What does an SSL verification error actually mean?

When your client opens an `https://` connection, the work happens in stages. First the TCP socket connects, then the TLS handshake negotiates a protocol version and cipher, and then — and only then — the server hands over its certificate. A verification error lives in that final stage: certificate validation.

To accept a certificate, your client runs a series of checks. The certificate must:

  1. Chain to a trusted Certificate Authority (CA). The server’s certificate is signed by an intermediate, which is signed (directly or through more intermediates) by a root CA your system already trusts. Your client follows that chain up to a root in its trust store.
  2. Be valid in time. The current date must fall between the certificate’s `notBefore` and `notAfter` dates — it must not be expired or not-yet-valid.
  3. Match the hostname. The domain you connected to must appear in the certificate’s Subject Alternative Names.
  4. Not be revoked. The CA must not have invalidated it.

If any of these checks fails, the client refuses the certificate and raises a verification error. The encryption may be perfectly fine; the question verification answers is not “is this connection encrypted?” but “is this the server it claims to be, vouched for by an authority I trust?”

This is distinct from a connection or handshake failure. If the socket never opens or the handshake breaks, you get a different error entirely — see and . A verification error is special precisely because everything *up to* the certificate worked.

Why do verification errors happen even when the certificate is “fine”?

This is the part that confuses people. The certificate on the server can be entirely valid — purchased from a reputable CA, unexpired, correctly issued for the domain — and you can *still* get a verification error. That is because verification is a two-sided agreement: the certificate has to be valid and your client has to be able to build a trusted path to it.

The break is frequently on the client side. A server, container, or minimal OS image with an outdated or missing CA bundle does not recognize newer root CAs, so a perfectly good certificate appears untrusted. Equally often the break is on the server side: the server sends its own certificate but omits the intermediate certificate, leaving your client with a gap it cannot bridge to a trusted root. Same symptom, opposite cause.

That is why the fix depends entirely on *where* the trust path breaks — and why blindly turning verification off is the wrong move every time.

What are the common causes and fixes?

The table below maps each message to its likely cause and the correct fix. The diagnostics in the next section tell you which row applies.

Symptom / message Likely cause Fix
`unable to get local issuer certificate` Missing/outdated CA bundle on the client *or* missing intermediate sent by the server Update `ca-certificates` on the client; if server-side, install the full chain (see below)
`certificate verify failed` (generic) Trust path could not be built for any of several reasons Run `openssl s_client` to see the exact `Verify return code`, then fix that specific cause
`self-signed certificate` / `self-signed certificate in certificate chain` Certificate signed by a CA not in your trust store (private/internal CA) Add the organization’s CA certificate to the system trust store
`certificate has expired` Expired certificate (or wrong client clock) Renew and reinstall the certificate; verify the system clock
`hostname mismatch` / `does not match` Certificate’s SANs do not include the domain you connected to Use the correct hostname, or reissue the certificate with the right SAN
Works in browser, fails in `git`/`curl`/app App uses its own CA bundle, not the browser’s; bundle is stale Update the app/runtime’s CA bundle or point it at the system store
Fails only on one old server/container Stale `ca-certificates` package `apt-get update && apt-get install –reinstall ca-certificates` (or `update-ca-trust` on RHEL)
Intermittent across multiple machines Server sometimes serves incomplete chain behind a load balancer Fix every node to send the full chain

The number one cause: an outdated CA bundle

On servers, Docker images, CI runners, and language runtimes, the most common reason a *valid* certificate fails verification is a stale CA certificate bundle. The trust store is just a file (or set of files) listing the root CAs the system accepts. If that file is old, it will not contain newer roots, and certificates that chain to them look untrusted.

On Debian and Ubuntu systems, refresh it with:

“`bash sudo apt-get update sudo apt-get install –reinstall ca-certificates sudo update-ca-certificates “`

On RHEL, CentOS, Rocky, and Alma:

“`bash sudo yum reinstall ca-certificates sudo update-ca-trust “`

This single step resolves a large share of `unable to get local issuer certificate` and `certificate verify failed` errors in updaters and package managers, because those tools rely on the system CA bundle to validate the endpoints they download from.

Untrusted or self-signed CA

If the certificate is issued by an internal or private CA — common inside companies, on staging servers, or behind corporate proxies that re-sign TLS — your client legitimately does not trust it, because that CA is not a public root. The correct fix is to add that CA’s certificate to the trust store, not to disable verification. On Debian/Ubuntu:

“`bash sudo cp internal-ca.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates “`

Now anything chaining to that CA verifies cleanly, and you keep verification fully enabled.

Incomplete certificate chain (server side)

If the server omits its intermediate certificate, clients that do not happen to cache that intermediate will fail with `unable to get local issuer certificate`. The fix is on the server: install the full chain (leaf + all intermediates, correctly ordered). For converting and assembling certificate files, see .

How do you diagnose where verification breaks?

Stop guessing and ask `openssl` directly. This is the single most useful command:

“`bash openssl s_client -connect example.com:443 -servername example.com “`

Read two things in the output:

  • `Verify return code:` at the bottom. `0 (ok)` means verification passed. Anything else names the exact problem — for example `20 (unable to get local issuer certificate)`, `21 (unable to verify the first certificate)`, or `10 (certificate has expired)`.
  • `Certificate chain` near the top. Count the certificates. If you see only the leaf (`s:` for your domain) with no intermediate above it, the server is not sending the full chain.

To inspect the certificate’s dates and hostname coverage:

“`bash echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \

| openssl x509 -noout -dates -subject -issuer -ext subjectAltName “`

This catches expiry (the dates), wrong-issuer/untrusted-CA problems (the issuer), and hostname mismatch (the SANs).

With `curl`, the verbose flag shows the verification step in context:

“`bash curl -v https://example.com 2>&1 | grep -iE “SSL certificate|issuer|subject|verify” “`

If `curl` reports `SSL certificate problem: unable to get local issuer certificate`, you are squarely in CA-bundle or incomplete-chain territory — the two most common causes above.

Why you should never just disable SSL verification

Here is the trap that the internet pushes you toward, and why it is dangerous. Every flag that “fixes” a verification error by silencing it — `curl -k`, `curl –insecure`, `wget –no-check-certificate`, `git config http.sslVerify false`, `PYTHONHTTPSVERIFY=0`, `NODE_TLS_REJECT_UNAUTHORIZED=0`, `-k` in countless SDKs — does not solve anything. It removes the check that verification is for. The error disappears because you told the client to stop asking whether the server is genuine. The data is still encrypted, but encryption to an *unverified* party is worthless: you have no idea who is on the other end. That is exactly the condition a man-in-the-middle attacker needs. Someone on the network path can present their own certificate, your client accepts it without complaint, and they decrypt, read, and re-encrypt everything — passwords, tokens, package payloads, source code — while both sides believe the channel is secure. A verification error is your security working correctly and warning you. The fix is to restore trust (update the CA bundle, add the internal CA, complete the chain, renew the certificate), not to abandon trust. Disabling verification in production turns a loud, fixable warning into a silent, exploitable hole.

The only defensible uses of `-k`-style flags are momentary, local debugging to confirm a diagnosis — never a deployed configuration, a script that runs unattended, or anything touching real users or credentials. If a fix instruction tells you to disable verification permanently, treat it as a red flag and find the real cause instead.

How DarazHost prevents verification errors before they start

Most server-side verification errors trace back to a certificate that was installed without its full chain, issued by a CA the world does not trust, or left to expire. The cleanest way to avoid all three is to host where valid, complete, trusted SSL is the default rather than something you assemble by hand.

DarazHost issues free SSL certificates through automated AutoSSL, provisioned with their complete, correctly ordered certificate chains — leaf plus every intermediate — so clients can always build a path to a trusted public root and verification simply passes. Because the certificates chain to widely trusted public CAs, there are no “self-signed” or “untrusted issuer” surprises for your visitors, your `curl` integrations, or your automated updaters. Renewals are automated, which removes the expired-certificate failure mode entirely.

For teams that manage their own trust requirements — adding an internal CA to a server’s trust store, refreshing the `ca-certificates` bundle on a build server, or pinning certificates for a private API — our VPS plans include full root access, so you control the trust store end to end. And whenever a chain, CA-bundle, or validation issue does appear, 24/7 expert support is on hand to read the `openssl` output with you and fix the root cause. The outcome: certificates that verify cleanly the first time, for every client that connects.

How do you fix the error once you know the cause?

The `Verify return code` points straight to the fix:

  • `unable to get local issuer certificate`, client side → update the CA bundle (`ca-certificates`).
  • `unable to verify the first certificate`, server side → install the full chain with all intermediates.
  • `self-signed certificate in certificate chain` → add the internal/private CA to the trust store.
  • `certificate has expired`renew and reinstall; check the system clock; automate renewal.
  • Hostname mismatch → connect by the correct hostname or reissue with the right SAN.
  • Wrong clock → sync time with NTP, then retry.

In every case the goal is the same: make the certificate verifiable, never make the client stop verifying.

Frequently asked questions

Is an SSL verification error the same as an SSL connect error or protocol error? No. A connect error means the TLS connection never opened, and a protocol error means the handshake itself failed (version or cipher mismatch). A verification error happens *after* both of those succeed: the certificate was received, but the client could not trust it. They are diagnosed and fixed differently, which is why mixing them up wastes hours. See the linked connect-error and protocol-error guides.

Why does the site verify fine in my browser but fail in `git`, `curl`, or my app? Browsers ship and update their own trust stores frequently, and they sometimes cache missing intermediate certificates from earlier visits. Command-line tools and language runtimes use a separate CA bundle that may be older, and they do not cache intermediates. So a missing intermediate or a stale bundle fails in `curl` while the browser quietly papers over it. Update the tool’s CA bundle and ensure the server sends the full chain.

What is the single most common cause of `unable to get local issuer certificate`? Either a stale CA bundle on the client or a missing intermediate certificate on the server. Run `openssl s_client` and count the certificates in the chain: if the server only sends the leaf, fix the server; if the chain looks complete but still fails, update the client’s `ca-certificates`.

Is it ever safe to use `curl -k` or `–no-check-certificate`? Only for momentary, local debugging to confirm what the error is — never in production, in unattended scripts, or anywhere real data flows. Those flags disable the very check that protects you from man-in-the-middle attacks. Restore trust instead of disabling it.

Could a wrong system clock cause a verification error? Yes. Validity checks compare the certificate’s dates against the current time. If the system clock is badly wrong, a perfectly valid certificate can appear expired or not yet valid. Sync the clock with NTP and retry before suspecting the certificate.

About the Author

Leave a Reply