Unable to get local issuer certificate: what it means and how to fix it
Unable to get local issuer certificate means a client could not connect the server's certificate to a root authority it trusts. Either the server did not send the intermediate certificate that links the two, or the client's own list of trusted roots lacks the one needed. The message comes from tools that use OpenSSL or similar libraries, such as curl, Git, npm, Node.js and Python, rather than from browsers, which is why a site can open normally in a browser and still fail here.
Quick facts
- Where it appears
- curl, Git, npm and Node.js, Python, PHP and other OpenSSL-based clients
- Typical text
SSL certificate problem: unable to get local issuer certificate- OpenSSL code
- verify error 20
- Who fixes it
- the server owner if the chain is incomplete; the client side if its trust store is missing a root
- Related messages
self-signed certificate in certificate chain,x509: certificate signed by unknown authority
What the error means
A server's certificate is signed by an intermediate authority, and the intermediate is signed by a root. The client holds a list of trusted roots, called a trust store or CA bundle, and has to build a path from the server's certificate up to one of them. See certificate chains for how that path works.
"Local issuer" means the next certificate up that path, which the client expected to find either in what the server sent or in its local store. It found neither, so the path stopped and the connection was refused.
The same fault appears with slightly different wording depending on the tool:
curl: (60) SSL certificate problem: unable to get local issuer certificate
[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)The first is curl and Git, the second Python, the third Node.js and npm.
Server or client: how to tell which
The fault is in one of two places, and one command tells them apart:
$ openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/nullCount the certificates in the output, each between BEGIN CERTIFICATE and END CERTIFICATE lines. A correctly configured server sends its own certificate and at least one intermediate. The last lines report the result, for example Verify return code: 0 (ok) or Verify return code: 20 (unable to get local issuer certificate).
Only one certificate is sent. The server is missing its intermediate. Every client that does not already hold that intermediate fails, and the fix is on the server.
The full chain is sent, but your tool still fails. The client side is missing a root. Common reasons are an outdated CA bundle, a tool that uses its own bundle instead of the operating system's, or a corporate proxy that re-signs traffic with the company's own root.
Browsers succeed while tools fail. Browsers fill gaps that command-line tools do not. They cache intermediates from earlier visits, and several fetch a missing one automatically. A missing intermediate therefore often shows up first in scripts, deployments and package installs.
Fix on the server: send the full chain
Configure the server to send the certificate together with its intermediates, in order: the site's certificate first, then the intermediate that signed it. Most authorities provide this as one file, often called the full chain or bundle. In nginx, ssl_certificate points to that combined file. In Apache 2.4.8 and later, SSLCertificateFile takes the same combined file. Reload the server and repeat the check above: the count should rise and the verify code should read 0.
The root itself does not need to be sent; clients already have it.
Fix on the client, by tool
When the server's chain is complete, the client has to be given the missing root. Get the root certificate in PEM format, either from the authority's website or, behind a corporate proxy, from whoever manages the proxy. Then point the tool at it. The settings below are documented by each project.
curl. Pass a bundle for one request with
--cacert /path/to/ca.pem, or set theCURL_CA_BUNDLEenvironment variable. See the curl documentation on certificates.Git.
git config --global http.sslCAInfo /path/to/ca.pemmakes Git trust that bundle for HTTPS remotes. On Windows,git config --global http.sslBackend schannelmakes Git use the Windows certificate store, which already holds roots installed by an administrator. See git config.Node.js. Set
NODE_EXTRA_CA_CERTS=/path/to/ca.pembefore starting the process; Node.js adds those certificates to its built-in list. See the Node.js documentation.npm.
npm config set cafile /path/to/ca.pemsets the bundle npm uses for the registry. See npm config.Python. The requests library uses the bundle from the certifi package by default. Update it with
pip install --upgrade certifi, or point requests at another bundle with theREQUESTS_CA_BUNDLEenvironment variable. See the requests documentation. On macOS, Python installed from python.org includes an Install Certificates command in its application folder, which installs certifi's bundle.
Why turning off verification is the wrong fix
Every tool on this page has a way to skip certificate checks, and search results recommend them often: curl -k, git config http.sslVerify false, NODE_TLS_REJECT_UNAUTHORIZED=0, verify=False in Python. They make the error disappear by removing the check that reported it.
What remains is an encrypted connection to an unverified party. For a package install or a deployment, that means accepting code from whoever can intercept the connection. The setting also tends to outlive the problem: once in a script or a shared configuration, it is rarely removed. Fix the chain or the trust store instead; both are a single change.
Related messages
self-signed certificate in certificate chain. The chain ends in a root the client does not trust. Behind a corporate proxy, this is the usual form of the error, because the proxy presents certificates issued by the company's own root. Add that root as above. If the server itself uses a self-signed root, see self-signed certificates.
x509: certificate signed by unknown authority. The same fault in programs written in Go, including Docker and Kubernetes tools. On Linux, Go reads the system trust store, so install the root there.
In browsers, the same fault appears as ERR_CERT_AUTHORITY_INVALID or SEC_ERROR_UNKNOWN_ISSUER. A certificate outside its validity dates is a separate fault, which Chrome reports as ERR_CERT_DATE_INVALID.
FAQ
What does "unable to get local issuer certificate" mean?
The client could not find the certificate that issued the server's certificate, either in what the server sent or in its own trust store. Without it, the client cannot link the certificate to a trusted root, so it refuses the connection.
How do I fix "unable to get local issuer certificate" in curl?
First check whether the server sends its intermediate with openssl s_client -showcerts. If it does not, the server must be fixed. If it does, give curl the missing root with --cacert /path/to/ca.pem or the CURL_CA_BUNDLE environment variable.
How do I fix it in Git?
Point Git at a bundle containing the missing root with git config --global http.sslCAInfo /path/to/ca.pem. On Windows, setting http.sslBackend to schannel makes Git use the Windows certificate store instead.
How do I fix it in npm or Node.js?
For npm, set the bundle with npm config set cafile /path/to/ca.pem. For Node.js applications, set NODE_EXTRA_CA_CERTS to the file's path before the process starts.
Why does the site work in my browser but not in curl?
Browsers cache intermediate certificates and can fetch a missing one, so they hide a server that sends an incomplete chain. Curl and similar tools do neither, so they report the gap.
Is it safe to disable SSL verification?
No. It removes the check rather than the cause, and leaves the connection open to interception. For anything that installs code or sends credentials, that is a real risk. Add the missing root or fix the server's chain instead.