If you're behind a corporate proxy or tunneling solution like ZScaler or Cloudflare WARP, you may encounter SSL issues like SELF_SIGNED_CERT_IN_CHAIN when running development tools, such as when you run commands like npm install or when your Node.js script attempts to make HTTPS calls.
Corporate proxy/tunneling solutions often perform HTTPS interception using a self-signed or internally-signed certificate. Typically, the IT or security team managing your device through MDM software will install this certificate in the operating system trust store. If the development tool does not follow the convention of trusting the root certificates in the operating system trust store, it needs to be explicitly configured to trust the corporate proxy/tunnelling solution self-signed or internally-signed certificate to avoid SSL verification errors.
If escaping the corporate proxy/tunnelling solution is not an option, you can extract these trusted certificates and explicitly configure your development tools to trust them.
If you have the PEM file that is used by your corporate proxy/tunnelling solution, you can use that PEM file in Step 2.
Use the Export-Windows-Certificates.ps1 PowerShell script to ump your systemβs root certificates into a PEM file at %USERPROFILE%\.certs\certs.pem
Use the export_macos_certificates.sh shell script to dump your systemβs root certificates into a PEM file at ${HOME}/.certs/certs.pem
Node.js uses a root certificate trust store bundled with the Node.js runtime. You can configure an environment variable NODE_EXTRA_CA_CERTS to trust self-signed SSL certificates in a PEM file.
π Reference: nodejs/node#4175
export NODE_EXTRA_CA_CERTS="${HOME}/.certs/certs.pem"β Use this when running
npm installor Node-based scripts that make HTTPS requests.
For curl:
export CURL_CA_BUNDLE="${HOME}/.certs/certs.pem"For wget:
wget --ca-certificate="${HOME}/.certs/certs.pem" https://example.comβ Useful when using CLI tools to pull packages or APIs behind a corporate proxy.
If you are using the requests library, you can configure an environment variable REQUESTS_CA_BUNDLE to trust self-signed SSL certificates in a PEM file. You may also need to install the pip-system-certs library.
Using environment variable:
export REQUESTS_CA_BUNDLE="${HOME}/.certs/certs.pem"In code:
import requests
import os
response = requests.get(
"https://example.com",
verify=f"{os.environ['HOME']}/.certs/certs.pem"
)β Also affects
pipifREQUESTS_CA_BUNDLEis set.
Programmatically load certificates:
package main
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"os"
)
func main() {
caCert, _ := ioutil.ReadFile(os.Getenv("HOME") + "/.certs/certs.pem")
roots := x509.NewCertPool()
roots.AppendCertsFromPEM(caCert)
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: roots},
},
}
resp, _ := client.Get("https://example.com")
defer resp.Body.Close()
}β Required only if you override the default
http.Client. Go usually respects system CA roots unless running in Alpine or custom containers.
Option 1: Import PEM into a Java keystore:
keytool -import -trustcacerts -file ~/.certs/certs.pem \
-keystore ${JAVA_HOME}/lib/security/cacerts \
-storepass changeit -alias corp-root -nopromptOption 2: Use JAVA_OPTS:
export JAVA_OPTS="-Djavax.net.ssl.trustStore=${HOME}/.certs/cacerts -Djavax.net.ssl.trustStorePassword=changeit"
β οΈ Java tools like Maven and Gradle use their own trust stores. You must either import the certs or configure the trust store explicitly.
Environment variable:
export SSL_CERT_FILE="${HOME}/.certs/certs.pem"In code:
require 'net/http'
require 'openssl'
uri = URI('https://example.com')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.ca_file = "#{ENV['HOME']}/.certs/certs.pem"
response = http.request(Net::HTTP::Get.new(uri))
puts response.bodyβ Works with most Ruby HTTP clients.
Build-time (Dockerfile):
COPY certs.pem /usr/local/share/ca-certificates/certs.pem
RUN update-ca-certificatesRuntime (docker-compose):
services:
myapp:
build: .
environment:
NODE_EXTRA_CA_CERTS: /usr/local/share/ca-certificates/certs.pemβ Ensure you update certificates inside containers too. Some base images (like Alpine) do not come with
ca-certificatesby default.
git config --global http.sslCAInfo "${HOME}/.certs/certs.pem"β Ensures that
git clone,git fetch, and other HTTPS actions work over corporate proxies.
- π Regenerate
certs.pemif your system trust store is updated (e.g., after certificate renewal). - π Do not share your PEM file publicly β it may contain internal CA certificates.
- π§° Consider adding the environment variable exports to your shell init file (
~/.bashrc,~/.zshrc, etc.). - π οΈ You can bundle this into a dev bootstrap script for teams to avoid local SSL errors.