You run curl, git clone, pip install, or docker pull on a corporate network and hit:
curl: (60) SSL certificate problem: unable to get local issuer certificateIn most corporate-network cases, this is not a broken curl package. It means the TLS certificate presented to your Linux host was signed by a corporate inspection CA your system does not trust yet. Windows and the browser often work because IT already pushed that root CA to managed PCs. Linux VMs, WSL, CI runners, and containers usually start with only public CAs.
This guide walks through diagnosis, installing the corporate root CA on Debian-family and RHEL-family distros, verifying the fix, and pointing common developer tools at the updated bundle. The Rocky Linux 10.2 lab output below comes from a real Zscaler inspection environment; the same steps apply when the issuer shows Netskope, Fortinet, Palo Alto, Cisco Umbrella, Cloudflare Gateway, or an internal AD CS CA.
Tested on: Rocky Linux 10.2 (Red Quartz); kernel 6.12.0-211.16.1.el10_2.0.1.x86_64; curl 8.12.1; OpenSSL 3.x.
Quick answer
| Family | Install path | Refresh trust | Default bundle for tools |
|---|---|---|---|
| Ubuntu, Debian | /usr/local/share/ca-certificates/*.crt |
sudo update-ca-certificates |
/etc/ssl/certs/ca-certificates.crt |
| RHEL, Rocky, AlmaLinux, Fedora | /etc/pki/ca-trust/source/anchors/*.crt |
sudo update-ca-trust extract |
/etc/pki/tls/certs/ca-bundle.crt |
Copy your organization's inspection root CA in PEM format, run the refresh command, then retry curl -v https://example.com. If only one application still fails, add the bundle path to that tool's CA setting (Git, Python, Node.js, Java, or containers).
Why this error happens
On the public internet, a site like github.com normally presents a certificate signed by a public CA (DigiCert, Sectigo, Google Trust Services, Let's Encrypt, and others). Your Linux trust store already includes those public roots, so verification succeeds.
Corporate secure web gateways and firewalls often perform SSL/TLS inspection (HTTPS interception):
- Your client starts TLS to what it thinks is
github.com. - The security appliance terminates TLS, inspects the traffic, and opens its own TLS session to the real server.
- The appliance returns a replacement certificate signed by a private corporate CA.
- If that corporate CA is not in the Linux trust store, OpenSSL reports
unable to get local issuer certificateandcurlexits with code 60.
Windows workstations and browsers frequently trust the inspection CA already (Group Policy, Zscaler Client Connector, Netskope client, and similar). A Rocky Linux VM, Ubuntu server, WSL distro, or Docker container may not inherit that trust automatically.
Common products or platforms involved in this pattern include Zscaler ZIA, Netskope, Cisco Umbrella, Cloudflare Gateway, Fortinet FortiGate SSL inspection, Palo Alto Networks decryption, Check Point HTTPS inspection, Sophos firewall scanning, and Microsoft AD CS–issued forward-trust CAs.
Symptoms across tools
The underlying failure is the same missing trust anchor; only the error text changes:
| Tool | Typical message |
|---|---|
| curl | curl: (60) SSL certificate problem: unable to get local issuer certificate |
| Git | SSL certificate problem: unable to get local issuer certificate |
| Python / pip | SSLError: CERTIFICATE_VERIFY_FAILED |
| npm | UNABLE_TO_GET_ISSUER_CERT_LOCALLY |
| Go | x509: certificate signed by unknown authority |
| Codex CLI | Login or device-auth HTTPS/WebSocket failures after install |
Reinstalling curl or upgrading CA packages without adding the corporate root does not fix inspection failures.
Step 1 — Check whether all HTTPS sites fail
Start with proxy environment variables. They change which path curl takes:
env | grep -i proxyhttps_proxy=http://proxy.example.com:8080
HTTPS_PROXY=http://proxy.example.com:8080If HTTP_PROXY or HTTPS_PROXY is set, curl may go through an explicit forward proxy. If they are unset, the network may still perform transparent TLS inspection. During diagnosis, add --noproxy '*' to mirror what tools like openssl s_client see on a direct TLS connection. For proxy variable setup, see set up http_proxy and https_proxy.
If routing and DNS look healthy but HTTPS still fails, work through the layered checks in check internet connection from the Linux command line before trusting a new root CA.
Test a site that may bypass inspection in your environment:
curl -Iv --noproxy '*' --max-time 15 https://www.google.com* subject: CN=www.google.com
* issuer: C=US; O=Google Trust Services; CN=WE2
* SSL certificate verify ok.Google still used a public CA in our lab, so verification succeeded even before we installed the corporate root.
Test a site your policy likely inspects:
curl -Iv --noproxy '*' --max-time 15 https://github.comBefore installing the corporate CA, the same command failed:
* TLSv1.3 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: unable to get local issuer certificate
curl: (60) SSL certificate problem: unable to get local issuer certificateAfter we trusted the corporate root (Step 5), the issuer changed to the inspection CA and verification succeeded:
* subject: CN=github.com; O=Zscaler Inc.; OU=Zscaler Inc.
* issuer: C=US; ST=California; O=Zscaler Inc.; OU=Zscaler Inc.; CN=Zscaler Intermediate Root CA (zscaler.net) (t)
* SSL certificate verify ok.If every HTTPS URL fails, check system time with timedatectl, confirm chrony or NTP has the clock in sync, and rule out a corrupt CA package or an unrecognized man-in-the-middle before trusting a new root. If only selected domains fail, SSL inspection is the likely cause.
Step 2 — Identify the certificate issuer
openssl s_client shows who signed the certificate your host actually receives. Pick a URL that fails under curl.
openssl s_client -connect github.com:443 -servername github.com </dev/null 2>/tmp/cert.log | openssl x509 -noout -subject -issuer -datessubject=CN=github.com, O=Zscaler Inc., OU=Zscaler Inc.
issuer=C=US, ST=California, O=Zscaler Inc., OU=Zscaler Inc., CN=Zscaler Intermediate Root CA (zscaler.net) (t)
notBefore=Jul 5 17:01:57 2026 GMT
notAfter=Jul 18 02:14:50 2026 GMTRead the issuer line:
- Public CA (Google Trust Services, DigiCert, Let's Encrypt, Sectigo) — normal direct TLS or bypassed inspection.
- Vendor or company name (Zscaler, Fortinet, Netskope, Palo Alto, or your organization) — SSL inspection; install the documented corporate root CA.
The verification log at the bottom confirms the missing anchor before you fix trust:
grep -E 'verify error|depth=' /tmp/cert.log | head -4depth=2 C=US, ST=California, O=Zscaler Inc., OU=Zscaler Inc., CN=Zscaler Intermediate Root CA (zscaler.net)
verify error:num=20:unable to get local issuer certificate
depth=1 C=US, ST=California, O=Zscaler Inc., OU=Zscaler Inc., CN=Zscaler Intermediate Root CA (zscaler.net) (t)
depth=0 CN=github.com, O=Zscaler Inc., OU=Zscaler Inc.curl -v also prints which CA file it uses — useful when a tool ignores your new certificate:
curl -v --noproxy '*' --max-time 10 https://github.com 2>&1 | grep -E 'CAfile:|CApath:'* CAfile: /etc/pki/tls/certs/ca-bundle.crt
* CApath: noneOn Debian and Ubuntu the default CAfile is /etc/ssl/certs/ca-certificates.crt instead.
Step 3 — Get the correct corporate root CA
Do not export the short-lived leaf certificate for github.com or auth.openai.com. Install the root CA (or the forward-trust CA your security team documents) that signs inspected traffic.
Sources:
- IT or security team portal (preferred)
- Windows:
certmgr.msc→ Trusted Root Certification Authorities → Certificates → right-click the corporate root → All Tasks → Export → Base-64 encoded X.509 (.CER) - macOS: Open Keychain Access, check the System and login keychains, find your organization's proxy or root CA certificate, then export it as PEM or CER
You need PEM text (-----BEGIN CERTIFICATE----- … -----END CERTIFICATE-----). If export gives binary DER, convert before installing:
openssl x509 -inform DER -in company-root-ca.cer -out company-root-ca.crt(no output on success)Check the file format when unsure:
file company-root-ca.cercompany-root-ca.cer: PEM certificateStep 4 — Install the CA on Ubuntu and Debian
Install tooling if the image is minimal:
sudo apt update
sudo apt install -y ca-certificates openssl curlca-certificates is already the newest version.
curl is already the newest version.
openssl is already the newest version.
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.Copy the PEM file into the local trust directory. The filename must end with .crt:
sudo cp company-root-ca.crt /usr/local/share/ca-certificates/company-root-ca.crt(no output on success)Rebuild the system bundle:
sudo update-ca-certificatesA successful run appends your CA and reports one added certificate:
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.Verify HTTPS again:
curl -Iv --max-time 15 https://github.com* SSL certificate verify ok.On an inspected network, the issuer may show your proxy or security gateway, such as Zscaler, Fortinet, Netskope, or an internal CA.
Confirm OpenSSL can build a chain against the Debian bundle:
openssl s_client -connect github.com:443 -servername github.com -CAfile /etc/ssl/certs/ca-certificates.crt </dev/null 2>&1 | grep 'Verify return code'After update-ca-certificates, you want Verify return code: 0 (ok).
Point long-running tools at the Debian bundle when needed:
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt(no output on success)Step 5 — Install the CA on RHEL, Rocky, AlmaLinux, and Fedora
On Rocky Linux 10.2 we reproduced the failure, installed the corporate root, and verified the fix.
Install packages:
sudo dnf install -y ca-certificates openssl curl p11-kit-trustPackage ca-certificates-2025.2.86-1.el10.noarch is already installed.
Package openssl-1:3.5.5-4.el10_2.x86_64 is already installed.
Package curl-8.12.1-4.el10.x86_64 is already installed.
Package p11-kit-trust-0.26.2-1.el10.x86_64 is already installed.
Dependencies resolved.
Nothing to do.
Complete!Copy the PEM certificate into the system trust anchors directory:
sudo cp /root/zscaler-root-ca.cer /etc/pki/ca-trust/source/anchors/zscaler-root-ca.crt(no output on success)Rebuild extracted bundles (including /etc/pki/tls/certs/ca-bundle.crt):
sudo update-ca-trust extract(no output on success)Confirm the anchor is trusted:
trust list | grep -i zscaler -A3label: Zscaler Root CA
trust: anchor
category: authorityRetry HTTPS:
curl -Iv --noproxy '*' --max-time 15 https://github.com* SSL certificate verify ok.Confirm OpenSSL can build a chain to the system bundle:
openssl s_client -connect github.com:443 -servername github.com -CAfile /etc/pki/tls/certs/ca-bundle.crt </dev/null 2>&1 | grep 'Verify return code'Verify return code: 0 (ok)update-ca-trust force-enable. On Rocky Linux 10 this command returned unknown command in our test:
update-ca-trust force-enableError: unknown command: 'force-enable', see 'update-ca-trust --help' for usage.On current RHEL-family systems, place the certificate under /etc/pki/ca-trust/source/anchors/ and run update-ca-trust extract.
Step 6 — Fix tools that ignore the system CA store
update-ca-certificates and update-ca-trust fix anything that reads the OS OpenSSL bundle (curl, wget, dnf, yum, and many Go binaries). These tools often keep their own trust store. Set persistent exports in /etc/profile.d/corp-ca.sh or your shell rc file only after the system bundle is correct.
curl documents CURL_CA_BUNDLE, SSL_CERT_FILE, and SSL_CERT_DIR as supported CA bundle options. Use them when a single tool still fails after the system store is updated:
export CURL_CA_BUNDLE=/etc/pki/tls/certs/ca-bundle.crt(no output on success)On Debian and Ubuntu, point CURL_CA_BUNDLE and SSL_CERT_FILE at /etc/ssl/certs/ca-certificates.crt instead.
Git
git config --global http.sslCAInfo /etc/pki/tls/certs/ca-bundle.crt(no output on success)Git also honors GIT_SSL_CAINFO, which overrides http.sslCAInfo when set:
export GIT_SSL_CAINFO=/etc/pki/tls/certs/ca-bundle.crt(no output on success)On Debian and Ubuntu, use /etc/ssl/certs/ca-certificates.crt for both settings.
Python and pip
From pip 24.2 onward, pip can use system certificates through truststore in addition to certifi. Older pip releases, virtual environments, requests-based tools, and custom Python apps may still need an explicit bundle path.
Check your pip and OpenSSL default paths:
python3 -m pip --versionpip 23.3.2 from /usr/lib/python3.12/site-packages/pip (python 3.12)python3 -c "import ssl; print(ssl.get_default_verify_paths())"DefaultVerifyPaths(cafile='/etc/pki/tls/cert.pem', capath='/etc/pki/tls/certs', openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/etc/pki/tls/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/etc/pki/tls/certs')On Debian and Ubuntu, cafile is typically /etc/ssl/certs/ca-certificates.crt.
For older pip, venvs, or apps that still fail:
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt(no output on success)You can also pass pip install --cert /path/to/bundle.pem for a single install session.
Node.js and npm
export NODE_EXTRA_CA_CERTS=/etc/pki/tls/certs/ca-bundle.crt(no output on success)Use a PEM file containing one or more trusted CA certificates—not a leaf certificate or a single-site .cer export.
Or persist with npm config set cafile /path/to/bundle.pem.
Java
keytool -importcert -alias corp-ca -file company-root-ca.crt -keystore "$JAVA_HOME/lib/security/cacerts" -storepass changeit -nopromptOn success, keytool reports that the certificate was added to the keystore.
Codex CLI
Codex login, HTTPS requests, and secure WebSockets can use a corporate CA bundle. Codex checks CODEX_CA_CERTIFICATE first; if it is unset, it falls back to SSL_CERT_FILE.
On RHEL-family systems:
export SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
export CODEX_CA_CERTIFICATE=/etc/pki/tls/certs/ca-bundle.crt(no output on success)On Debian-family systems, use /etc/ssl/certs/ca-certificates.crt for both variables.
codex login --device-authWhen the corporate CA is trusted, Codex prints a device-auth URL instead of an SSL error. Add the exports to ~/.bashrc only after curl -v reports SSL certificate verify ok for an inspected site.
Docker, WSL, and containers
Container images ship their own /etc/ssl/certs tree. Installing the CA on Rocky or Ubuntu does not update an already-built Debian, Alpine, or Ubuntu container layer.
Options:
- Copy the corporate
.crtinto the image and runupdate-ca-certificates(Debian/Ubuntu) orupdate-ca-trust extract(RHEL-based images) duringdocker build. - For Alpine-based images, install
ca-certificateswithapkand runupdate-ca-certificatesinside the image:
RUN apk add --no-cache ca-certificates
COPY company-root-ca.crt /usr/local/share/ca-certificates/company-root-ca.crt
RUN update-ca-certificates- Mount the host bundle read-only and set
SSL_CERT_FILEorCURL_CA_BUNDLEin the container environment. - For WSL, install the CA inside the WSL distro — the Windows certificate store is separate. New VirtualBox or VMware VMs also need the corporate CA installed on the guest OS even when NAT provides internet access.
If docker pull or docker build still fails after fixing the host, rebuild the image with the CA or pass NODE_EXTRA_CA_CERTS / REQUESTS_CA_BUNDLE into build stages that call npm or pip.
Why not to disable SSL verification
Forum threads often suggest bypass flags. They hide the trust problem and weaken every connection in that shell or config:
| Shortcut | Risk |
|---|---|
curl -k / --insecure |
No server authentication for that request |
git config --global http.sslVerify false |
All Git HTTPS remotes skip verification |
pip install --trusted-host … |
Hostname allow-list, not full chain validation |
npm config set strict-ssl false |
npm stops validating registry TLS |
NODE_TLS_REJECT_UNAUTHORIZED=0 |
Disables TLS verification for Node processes |
Use bypass flags only for a one-shot test to see whether TLS inspection is the blocker — then install the corporate CA properly. Only install CAs supplied by your security team or official IT documentation.
Troubleshooting checklist
| Symptom | Likely cause | Fix |
|---|---|---|
curl: (60) only on some hosts |
Selective SSL inspection | Install corporate root CA; inspected sites will show vendor issuer in openssl s_client |
curl works with proxy env set, fails with --noproxy '*' |
Forward proxy vs transparent inspection paths differ | Diagnose with --noproxy '*'; align HTTP_PROXY and CA trust per IT guidance |
update-ca-certificates reports does not contain a certificate |
DER file renamed to .crt |
Convert with openssl x509 -inform DER … (Step 3) |
trust list shows CA but curl still fails |
Wrong cert (leaf/intermediate only) | Export the root CA; ask IT for the forward-trust bundle |
force-enable unknown command |
Old RHEL 7-era guide on modern Rocky/RHEL | Place cert in /etc/pki/ca-trust/source/anchors/ and run update-ca-trust extract |
| Host fixed, container still fails | Container CA bundle unchanged | Add CA inside image or mount bundle; set SSL_CERT_FILE in container |
verify error:num=10 certificate expired |
System clock wrong or expired leaf | Run timedatectl; fix chrony or NTP sync before trusting new roots |
| Issuer is public CA but chain fails | Server missing intermediate | Server-side chain issue — not corporate inspection |
References
- curl TLS certificate verification —
CURL_CA_BUNDLE,SSL_CERT_FILE,SSL_CERT_DIR, and usingcurl -vto debug trust - pip HTTPS certificates — truststore behavior from pip 24.2 onward
- Ubuntu Server — install a root CA certificate —
update-ca-certificatesworkflow - Red Hat Enterprise Linux 9 — shared system certificates —
update-ca-trustandtrustcommands - Zscaler — Software Developer TLS Solution (PDF) — inspection overview and Linux trust-store paths
- OpenSSL s_client manual — live TLS handshake inspection
- View a certificate with OpenSSL — PEM/DER, chains, and remote inspection
Summary
curl: (60) SSL certificate problem: unable to get local issuer certificate on Linux usually means a corporate SSL inspection product is signing HTTPS traffic with a private root CA that is not in your distro trust store yet. Confirm with openssl s_client, install the corporate root CA on Debian (update-ca-certificates) or RHEL (update-ca-trust extract), verify with curl -v, then configure Git, Python, Node.js, Java, containers, or Codex to use the updated bundle. Avoid permanent -k or sslVerify false shortcuts — trust the corporate CA intentionally instead.

