Proxy Certificate

Some Evomi features need to read the responses passing through the proxy rather than tunnelling them untouched. Response Caching is one of them: to store a stylesheet or an image, the proxy has to see it.

For HTTPS that means the connection is terminated at our proxy and re-signed on the fly with the Evomi Proxy CA. Your client has no reason to trust that certificate, so it aborts the handshake until you tell it to.

There are two ways to handle it and neither is more correct than the other:

Skip verification Trust the Evomi CA
Setup One flag or variable per client Download and install once
TLS verification Off for that client Stays on
Suits Scraping and automation against public pages Shared machines, or processes that also talk to your own services

Most people scraping public pages take the first option, because it is a single flag and there is nothing to install or keep track of. Pick the second if the same process handles anything you actually need certificate guarantees for.

â„šī¸

Only needed for cache-enabled requests

Standard proxying tunnels HTTPS end to end and needs neither option. You only need this page if your proxy password uses _cache-.

Option 1: Skip Certificate Verification

Every HTTP client has a switch for this. Nothing to download:

CURL:

curl -k -x http://testuser:[email protected]:1000 https://ip.evomi.com/s

-k and --insecure are the same flag.

import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

proxies = {
"https": "http://testuser:[email protected]:1000",
}
response = requests.get("https://ip.evomi.com/s", proxies=proxies, verify=False)
print(response.text)
// Per client, which keeps the rest of the process verifying as normal
const agent = new https.Agent({ rejectUnauthorized: false });

// Or for the whole process
// export NODE_TLS_REJECT_UNAUTHORIZED=0
transport := &http.Transport{
  Proxy: http.ProxyURL(proxyURL),
  TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: transport}
// Puppeteer
const browser = await puppeteer.launch({
args: ['--proxy-server=http://rp.evomi.com:1000', '--ignore-certificate-errors'],
});

// Playwright
const context = await browser.newContext({ ignoreHTTPSErrors: true });

// Selenium (Chrome)
options.addArguments('--ignore-certificate-errors');

This is fine for scraping public pages, where you are reading content rather than trusting it. The trade-off is that the client then accepts any certificate from any host, so it can no longer detect interception by anyone else on the path. Keep it scoped to the code doing proxy work and don’t apply it to code that also handles logins, payments or your own APIs — in Node and Go the per-client form above does exactly that, leaving the rest of the process verifying normally.

Option 2: Trust the Evomi CA

This keeps certificate verification switched on, so your client still rejects anything that is not signed by us or by a public CA.

Download

curl -O https://cdn.evomi.com/downloads/evomi.crt
Field Value
Common Name Evomi Proxy CA
Format PEM
Valid until 6 January 2036

Verify the download before installing it:

openssl x509 -in evomi.crt -noout -fingerprint -sha256

The SHA-256 fingerprint must be:

E3:75:7D:4F:CB:87:9F:6A:52:24:92:0F:20:93:8F:D4:89:D1:92:01:87:C7:EA:E4:3C:40:68:C2:4D:CC:1F:42

Per-Tool Setup

Most clients accept an extra CA through an environment variable or a single argument, which applies to that process only:

CURL:

# Per command
curl --cacert evomi.crt -x http://testuser:[email protected]:1000 https://ip.evomi.com/s

# Or for every command in the shell session
export CURL_CA_BUNDLE=/path/to/evomi.crt
import requests

proxies = {
"https": "http://testuser:[email protected]:1000",
}

# verify accepts a path to a CA bundle
response = requests.get("https://ip.evomi.com/s", proxies=proxies, verify="evomi.crt")
print(response.text)

# httpx and aiohttp take an ssl.SSLContext instead:
#   ctx = ssl.create_default_context(cafile="evomi.crt")
# Node reads additional CAs from this variable at startup
export NODE_EXTRA_CA_CERTS=/path/to/evomi.crt
node scraper.js
# Go's crypto/x509 honours these on Unix systems
export SSL_CERT_FILE=/path/to/evomi.crt
go run main.go
# Import into a dedicated truststore, leaving the JDK default untouched
keytool -importcert -alias evomi-proxy-ca -file evomi.crt \
-keystore evomi-truststore.jks -storepass changeit -noprompt

java -Djavax.net.ssl.trustStore=evomi-truststore.jks \
   -Djavax.net.ssl.trustStorePassword=changeit -jar scraper.jar

Chromium ignores all of the above and reads the operating system trust store, so headless browsers need the system-wide install below — or the --ignore-certificate-errors flag from Option 1, which is why most browser automation ends up using that instead.

System-Wide Installation

âš ī¸

Scope the trust as narrowly as you can

Adding any root CA to a machine’s system trust store lets certificates signed by it be accepted for every host that machine talks to, not only traffic you send through Evomi.

Prefer the per-tool options above, which apply to a single process, and prefer a dedicated machine or container for proxy work over your daily-driver workstation. Install system-wide only when a tool gives you no other option.

macOS:

sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain evomi.crt

To remove it later: sudo security delete-certificate -c "Evomi Proxy CA" /Library/Keychains/System.keychain

# Run as Administrator
certutil -addstore -f "ROOT" evomi.crt

# To remove
certutil -delstore "ROOT" "Evomi Proxy CA"
sudo cp evomi.crt /usr/local/share/ca-certificates/evomi.crt
sudo update-ca-certificates

# To remove
sudo rm /usr/local/share/ca-certificates/evomi.crt
sudo update-ca-certificates --fresh

The file must keep the .crt extension and PEM encoding or it is skipped silently.

sudo cp evomi.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust

# To remove
sudo rm /etc/pki/ca-trust/source/anchors/evomi.crt
sudo update-ca-trust
â„šī¸

Certificate renewal

The CA is valid until January 2036, so no rotation is required in normal use. If we ever need to replace it early we will announce it in advance — the download URL stays the same, so re-running the steps above is all that is needed.

Verifying Setup

Send one request through a cache-enabled password. A response means the certificate is being accepted:

# If you skipped verification
curl -k -x http://testuser:[email protected]:1000 https://ip.evomi.com/s

# If you trusted the CA
curl --cacert evomi.crt -x http://testuser:[email protected]:1000 https://ip.evomi.com/s

Troubleshooting

Error Fix
certificate signed by unknown authority Neither option is active for this client — apply one of them
unable to get local issuer certificate (curl) Add -k, or pass --cacert evomi.crt
SSLCertVerificationError (Python) Set verify=False or verify="evomi.crt" on the request
ERR_CERT_AUTHORITY_INVALID (Chromium) Launch with --ignore-certificate-errors, or install the CA system-wide
Handshake fails with no HTTP error at all Expected when the certificate is rejected — the connection closes during the handshake, before any response
Works in curl but not in your app The app has its own trust store; check for a bundled CA file such as certifi
Trusted the CA but a tool still refuses it That tool likely reads the system store rather than the environment variable — install system-wide or skip verification for it