“unable to get local issuer certificate”: why it happens and how to fix it properly

Your program got a certificate from the server, tried to follow the chain of signatures up to a root it trusts, and got stuck: it could not find the certificate that signed the last one. The website may be perfectly fine; the gap is usually on your side.

What the error looks like

curl: (60) SSL certificate problem: unable to get local issuer certificate
requests.exceptions.SSLError: ... [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
Error: unable to get local issuer certificate   (code: 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY')
npm ERR! code UNABLE_TO_GET_ISSUER_CERT_LOCALLY
fatal: unable to access 'https://…': SSL certificate problem: unable to get local issuer certificate

A close cousin is self signed certificate in certificate chain (SELF_SIGNED_CERT_IN_CHAIN): the chain did reach a root, but it’s a root you don’t trust. Both have the same fixes.

How the chain is supposed to work

A server sends its own certificate (the leaf) plus one or more intermediate certificates. Each one is signed by the next, ending at a root certificate that ships inside your operating system or your tool. “Local issuer” means “the issuer I have locally”: the client walked up the chain and the next certificate it needed was neither sent by the server nor in its local store.

The four usual causes

CauseClueFix
A company proxy, VPN or antivirus inspects HTTPSFails on every site at work, works at home; the issuer is your company or security product, not a public CAGet the company root certificate (IT usually publishes it) and add it to each tool’s trust store (below)
No CA certificates in the container or minimal OSWorks on your laptop, fails in a slim Docker image or a fresh serverapt-get install -y ca-certificates (Debian/Ubuntu) or apk add ca-certificates (Alpine)
The tool uses its own CA bundle, not the OS oneBrowser and curl work, but Python, Node or Java failPoint that tool at the right bundle, or install the macOS Python certificates (below)
The server doesn’t send its intermediateFails for everyone on that one site, often only in scripts (browsers sometimes fetch or cache missing intermediates)Server owner: serve the full chain file (for example fullchain.pem, not cert.pem)

See the chain for yourself

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null

Read the s: (subject) and i: (issuer) lines from the top. If the last certificate’s issuer is something like Corp-Proxy Root CA or your antivirus, it’s interception. If the server sent only one certificate and the output ends with Verify return code: 20 (unable to get local issuer certificate) while other sites work, the server is missing its intermediate. Each certificate between the BEGIN and END lines is just Base64 text; paste one into the What is this? detector or the Base64 decoder to see it isn’t secret.

Trust the extra root, tool by tool

Save the root certificate as a PEM file (text starting with -----BEGIN CERTIFICATE-----), then:

Careful: some variables replace the default list instead of adding to it. REQUESTS_CA_BUNDLE pointing at a file with only your company root will break every other site; point it at the full system bundle after adding your root to the system store.

Why not just turn verification off?

curl -k, verify=False, NODE_TLS_REJECT_UNAUTHORIZED=0, npm config set strict-ssl false and git -c http.sslVerify=false all make the error disappear, and also make your program accept a fake certificate from anyone on the network. They tend to stay in scripts for years. Fix the trust store instead; it takes the same five minutes.

Look-alike errors

Only Python on a Mac fails while the browser is fine? See Python CERTIFICATE_VERIFY_FAILED on macOS.

Related

Cloudflare Error 525 · Cloudflare Error 526 · self-signed certificate in certificate chain · All guides

Questions

What does unable to get local issuer certificate mean?

Your program could not find the certificate authority that signed the server's certificate in its local trust store, so it cannot verify the chain. Usually a company proxy root, a missing CA package or a missing intermediate certificate is to blame.

Why does it work in my browser but not in Python or Node?

Browsers use the operating system's certificate store and can fetch missing intermediates. Python requests uses the certifi bundle and Node uses its own built-in list, so a company root added to your OS is not seen by them. Point them at it with REQUESTS_CA_BUNDLE or NODE_EXTRA_CA_CERTS.

Is it safe to set NODE_TLS_REJECT_UNAUTHORIZED=0 or verify=False?

No. It disables certificate checking entirely, so anyone between you and the server could impersonate it. Use it only for a quick local test, never in code you keep.

How do I fix it in a Docker container?

Install the CA package in the image (apt-get install ca-certificates or apk add ca-certificates). If you are behind a company proxy, also copy the company root certificate into /usr/local/share/ca-certificates and run update-ca-certificates.

Published .

Try it

Related guides

Newest guides