Skip to content
Commitpedia
Log in Join & edit

Fix: SSL certificate problem: unable to get local issuer certificate

curl, git, pip or npm cannot chain the server's certificate to a root they trust. Usual causes: a corporate proxy that re-signs TLS, a server missing its intermediate certificate, or an outdated CA bundle on the client.

Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?

Edit history of Fix: SSL certificate problem: unable to get local issuer certificate: 1 recorded edit
Edit history · 1 edit
Type
Article
Edits
1
Editors
1
First issued
15 Oct 2026
Last edit
15 Oct 2026
Reading
4 min
curl: (60) SSL certificate problem: unable to get local issuer certificate
fatal: unable to access 'https://github.com/org/repo.git/': SSL certificate problem: unable to get local issuer certificate
pip: Could not fetch URL https://pypi.org/simple/requests/: There was a problem confirming the ssl certificate: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
npm ERR! code UNABLE_TO_GET_ISSUER_CERT_LOCALLY
Error: unable to verify the first certificate (UNABLE_TO_VERIFY_LEAF_SIGNATURE)

Your client received a certificate from the server and tried to follow its chain of issuers up to a root certificate it trusts. Somewhere in that chain a certificate was missing or unknown, so the client refused to continue. Either the server sent an incomplete chain, or something between you and the server replaced the certificate, or your trust store is missing the root.

Step 1: look at the chain you actually receive

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | grep -E 'subject=|issuer=|Verify return code'

curl -vI https://example.com 2>&1 | grep -iE 'issuer|subject|SSL certificate|verify'

If the issuer is your company’s name or something like Zscaler, Netskope, Fortinet, Palo Alto or Cisco Umbrella, go to Cause 1. If the issuer is a public CA but only one certificate is shown, go to Cause 2. If the issuer is a public CA and the chain is complete, go to Cause 3.

Cause 1: a corporate proxy or security product re-signs TLS

Many company networks and endpoint agents intercept HTTPS and present their own certificate. Browsers work because the company root is installed in the OS store, but curl, git, Python and Node use their own bundles. Get the company root certificate (ask IT, or export it from your browser’s certificate viewer) and install it where each tool looks.

Check before you run this

  • Runs with administrator rights. Make sure you understand every line.
# System store (then most tools that use the system bundle work)
sudo cp corp-root.crt /usr/local/share/ca-certificates/corp-root.crt && sudo update-ca-certificates      # Debian, Ubuntu
sudo cp corp-root.crt /etc/pki/ca-trust/source/anchors/ && sudo update-ca-trust                          # RHEL, Fedora
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain corp-root.crt        # macOS

# Tools with their own bundle
git config --global http.sslCAInfo /etc/ssl/certs/ca-certificates.crt      # git on Linux after the step above
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt               # Python requests
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt                    # Python ssl, many others
pip config set global.cert /etc/ssl/certs/ca-certificates.crt              # pip
export NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/corp-root.crt   # Node and npm
export CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt                    # curl

The certificate must be in PEM format (text starting with -----BEGIN CERTIFICATE-----). Convert DER with openssl x509 -inform der -in corp.cer -out corp-root.crt.

Cause 2: the server does not send its intermediate certificate

Public CAs sign your certificate with an intermediate, and the server must send that intermediate along with the leaf. Browsers often fetch missing intermediates on their own, which hides the problem; command-line tools do not. If you run the server, serve the full chain; if you do not, tell its owner.

# nginx: ssl_certificate must be the full chain (leaf + intermediates)
cat server.crt intermediate.crt > fullchain.pem
# Let's Encrypt via certbot already provides fullchain.pem; use that, not cert.pem

# Apache 2.4.8+: SSLCertificateFile with the full chain, or SSLCertificateChainFile on older versions

# Verify from outside
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | grep 'Verify return code'
# Verify return code: 0 (ok)

Cause 3: the client's CA bundle is outdated

Old systems do not know newer roots. The classic case is Let’s Encrypt’s ISRG Root X1 on systems older than about 2017, or Docker images built from a minimal base without any certificates at all (ca-certificates not installed). Update the bundle.

Check before you run this

  • Runs with administrator rights. Make sure you understand every line.
sudo apt update && sudo apt install --only-upgrade ca-certificates      # Debian, Ubuntu
sudo dnf upgrade ca-certificates                                         # RHEL, Fedora
pip install --upgrade certifi                                            # Python's bundled roots

# Alpine and minimal images
apk add --no-cache ca-certificates && update-ca-certificates

What not to do

Careful: curl -k, git config --global http.sslVerify false, npm config set strict-ssl false, pip --trusted-host and NODE_TLS_REJECT_UNAUTHORIZED=0 all turn verification off. They make the error disappear by trusting whoever is in the middle, which is exactly what TLS is meant to prevent, and they tend to end up in CI configs permanently. Use them only for a single diagnostic run.

Check that it is fixed

curl -sS -o /dev/null -w '%{http_code}\n' https://example.com      # 200, no error
git ls-remote https://github.com/org/repo.git | head -n 1
python3 -c "import urllib.request; urllib.request.urlopen('https://pypi.org').read(1)" && echo ok

When it is not what you think

  • certificate has expired or certificate is not yet valid: check the server’s expiry, and the client’s clock; a VM or Raspberry Pi with a wrong date fails every certificate.
  • hostname mismatch or no alternative certificate subject name matches: the certificate is valid but for another name; you are using an IP, an internal alias or a CDN edge that does not list it.
  • self-signed certificate is a different code (DEPTH_ZERO_SELF_SIGNED_CERT): the server uses its own certificate; install that specific certificate as trusted, or get a real one.
  • If only one tool fails on a machine where others work, that tool has its own bundle or honors an environment variable such as SSL_CERT_FILE that points to a stale or missing file: env | grep -i -E 'ca_bundle|cert_file|ca_certs'.

Official documentation

Did this work for you?

No votes yet. Be the first.

If it did not work, the report form below tells the editors what went wrong.

See also

  • 0019 fetch with timeout, retries and proper error handling A reusable fetchJson helper for Node 20+ and browsers that adds a timeout, retries with backoff for temporary failures, and clear errors… Intermediate · Step by step · Any system Script Async code · Making requests 15 Oct 2026
  • 0040 Fix: error: failed to push some refs to … Git refused your push, usually because the remote branch has commits you do not have yet. Bring them in, combine them with… Beginner · Fixes an error · Any system Article Common errors 15 Oct 2026
  • 0041 Fix: CONFLICT (content): Merge conflict in … Two branches changed the same lines and Git cannot choose for you. Open the file, keep the right text, remove the markers,… Intermediate · Fixes an error · Any system Article Branching & rebasing · Common errors 15 Oct 2026
  • 0042 Fix: fatal: refusing to merge unrelated histories Your local repository and the remote do not share a single commit, usually because both were started separately. Allow the one-time merge,… Intermediate · Fixes an error · Any system Article Common errors 15 Oct 2026
  • 0081 How an HTTP request works: methods, headers, and body An HTTP request is a short text message a client sends to a server: a method and path, some headers, and sometimes… Beginner · Explains an idea · Any system Article Making requests 15 Oct 2026
  • 0083 Test a JSON API with curl A runnable bash script showing the curl commands you need most when testing an API: GET, POST JSON, custom headers, Bearer tokens,… Beginner · Step by step · Linux, macOS, Windows Script Authentication · Making requests 15 Oct 2026

Repos in the wild

  • Requests: HTTP for humans psf/requests Requests is the classic Python library for calling web APIs and downloading pages, with a simple API for GET and POST requests, headers, JSON and sessions. GitHub
  • Axios axios/axios A promise-based HTTP client for browsers and Node.js with interceptors, automatic JSON handling, request cancellation and a consistent API in both environments. GitHub
  • curl and libcurl (curl/curl) curl/curl The source of curl, the command-line tool for transferring data with URLs, and libcurl, the C library behind it that many other programs use for HTTP. GitHub
  • certbot: the Let’s Encrypt client certbot/certbot The EFF's ACME client that obtains free TLS certificates from Let's Encrypt, can configure nginx and Apache for HTTPS automatically and renews everything on a timer. GitHub

History

Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.

  1. Dominik +6,349 chars

Report a problem

Something wrong, unsafe or copied? Tell the editors.
What is wrong?