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 -sha256The 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:42Per-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.crtimport 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.jarChromium 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.crtTo 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 --freshThe 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-trustCertificate 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/sTroubleshooting
| 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 |