# SSL certificate verify failed: unable to get local issuer certificate
The client could not build a trust chain from the server's certificate to a CA it trusts. Usually the server is not sending its intermediate certificate, or the client has no CA store at all — and the two look identical from the error message while needing opposite fixes.
| Error | certificate verify failed: unable to get local issuer certificate |
|---|---|
| Applies to | OpenSSL 1.1.1 and later · Python ssl / requests 3.6 and later · curl all versions |
| Primary cause | The server does not send its intermediate certificate |
| First check | openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>&1 | grep -E 'depth=|verify (error|return)|s:|i:' |
| Confidence | medium2 sources, 2 primary |
| Verified | 2026-08-08fresh0d old · recheck by 2027-08-08 |
| Domain | securitytls ssl certificates ca-bundle openssl containers |
## Error
certificate verify failed: unable to get local issuer certificateCodes: CERTIFICATE_VERIFY_FAILEDUNABLE_TO_GET_ISSUER_CERT_LOCALLYSELF_SIGNED_CERT_IN_CHAIN
Also seen as: SSL: CERTIFICATE_VERIFY_FAILED · unable to verify the first certificate · x509: certificate signed by unknown authority · self signed certificate in certificate chain
## Problem
An HTTPS request fails from one client while a browser loads the same URL without complaint. It works on a developer machine and fails inside a container, or works from curl and fails from the application. Disabling verification makes it work, which is tempting and removes the protection the connection existed to provide.
## Root Cause6 known causes, ranked
- 01
The server does not send its intermediate certificate
primaryA chain is valid only if the client can link the leaf to a trusted root. Browsers often paper over a missing intermediate by fetching or caching it; other clients do not, which is exactly why the browser succeeds and curl does not.
→ how to tell: openssl s_client shows a chain of depth 0 only, or reports the verify error while the same host loads in a browser
- 02
The client has no CA store, or an outdated one
primaryMinimal container images ship without ca-certificates. The client then trusts nothing at all, so every public certificate fails — not just one host.
→ how to tell: Every HTTPS host fails rather than one, and /etc/ssl/certs is missing or nearly empty inside the container
- 03
A corporate proxy re-signs traffic with its own CA
commonTLS-inspecting middleboxes present a certificate signed by an internal CA. Machine trust stores are provisioned with it; language runtimes and containers with their own bundles are not.
→ how to tell: The issuer shown by openssl s_client is an internal or vendor name rather than a public CA, and only happens on the corporate network
- 04
The certificate is self-signed
commonDevelopment environments, internal services and appliances often use one. Nothing in any public CA store can vouch for it, by design.
→ how to tell: The subject and issuer of the certificate are the same, and the error mentions a self-signed certificate
- 05
The system clock is wrong
edgeValidation includes the notBefore and notAfter dates. A container or VM whose clock has drifted far enough sees a valid certificate as not yet valid or expired.
→ how to tell: date on the failing host differs materially from real time, and the error mentions expiry or a not-yet-valid certificate
- 06
A pinned or custom bundle omits the needed root
edgeAn application pointed at its own CA file via REQUESTS_CA_BUNDLE, SSL_CERT_FILE or a language-specific setting uses that file exclusively — the system store is then irrelevant.
→ how to tell: An environment variable naming a CA file is set, and unsetting it changes the outcome
## Solution
- 01Inspect the chain the server actually presents. This distinguishes a missing intermediate from a missing client trust store in one command.
$ openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>&1 | grep -E 'depth=|verify (error|return)|s:|i:'note: Read the depths: a chain ending at depth 0 means the server sent only its leaf. A chain reaching a root that your client does not recognise means the trust store is the problem.
- 02If the server is yours and the intermediate is missing, fix the server. This is the correct place — every client benefits, and none needs configuring.nginx
# nginx: the file must contain leaf + intermediates, in that order ssl_certificate /etc/ssl/certs/fullchain.pem; ssl_certificate_key /etc/ssl/private/privkey.pem;note: Use fullchain.pem, not cert.pem. This single mistake accounts for most missing-intermediate cases.
- 03If the client has no trust store, install one. In a minimal image this is a one-line fix.dockerfile
# Debian/Ubuntu RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \ && rm -rf /var/lib/apt/lists/* # Alpine RUN apk add --no-cache ca-certificates - 04For a corporate CA or an internal service, add that CA to the trust store rather than disabling verification. Trusting one more issuer keeps every other check intact.bash
# system-wide, Debian/Ubuntu cp corporate-ca.crt /usr/local/share/ca-certificates/ update-ca-certificates # per-process, when the system store cannot be changed export SSL_CERT_FILE=/path/to/ca-bundle.pem export REQUESTS_CA_BUNDLE=/path/to/ca-bundle.pem export NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.crt - 05Check the clock before assuming the certificate or the store is at fault.
$ date -u && openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates - 06Never ship disabled verification. If you must bypass it to isolate the cause, do so in a throwaway command and not in code — an unverified TLS connection provides encryption without authentication, which is the property that made it worth having.
$ curl -vk https://example.com # diagnosis only, never in an application
verify · openssl s_client reports 'Verify return code: 0 (ok)' from the same host and container that failed, and the application's request succeeds with verification enabled.
if that fails · Where a third-party endpoint genuinely presents an incomplete chain and cannot be fixed, add the specific missing intermediate to your own trust bundle for that host rather than disabling verification globally — the trust decision then stays explicit and auditable.
## Applies To
- OpenSSL
- 1.1.1 and later— Verification requires a complete chain to a locally trusted root.
- Python ssl / requests
- 3.6 and later— Client contexts default to CERT_REQUIRED and load default CA certificates unless a bundle is supplied.
- curl
- all versions— Performs certificate verification by default, including hostname matching.
- Platforms
- linux, macos, containers
## Not Applicable Tonear misses this page does not answer
- ✗ Hostname mismatch errors, where a chain verified but the certificate does not cover the name requested
- ✗ Expired certificates, which report expiry rather than a missing issuer
- ✗ TLS version or cipher negotiation failures, which fail before any certificate is examined
- ✗ Client-certificate authentication failures, where the server rejects the client's certificate rather than the reverse
## Evidence2 sources
https://curl.se/docs/sslcerts.html
curl · read 2026-08-08
supports: That verification is on by default and checks both signature and server name, and that a self-signed certificate, a missing CA store, or a CA absent from the store used all produce the same verification failure — which is why the error text alone does not identify the cause.
“if you do not install a CA cert store, if the server uses a certificate signed by a CA that is not included in the store you use”
https://docs.python.org/3/library/ssl.html
Python Software Foundation · read 2026-08-08
supports: That a client-purpose context requires certificate validation and loads default CA certificates unless an explicit bundle is given — so a supplied bundle replaces the system store rather than supplementing it.
“sets verify_mode to CERT_REQUIRED”
## Confidence
mediumThat verification is on by default and that a missing CA store, an unknown CA and a self-signed certificate produce the same failure are quoted from curl's own documentation; the client-side trust behaviour from Python's ssl reference. Confidence is medium rather than high because it rests on two sources: the missing-intermediate diagnosis via s_client depths, the corporate-proxy case and the per-runtime environment variables are operational practice rather than statements quoted here.