# Proxy Certificate

Source: https://docs.evomi.com/proxy-instructions/integration-guides/proxy-certificate/

Some Evomi features need to read the responses passing through the proxy rather than tunnelling them untouched. [Response Caching](/proxy-instructions/residential-proxies/expert-settings/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

  ```bash
curl -k -x http://rp.evomi.com:1000 -U testuser:testpassword_cache-all https://ip.evomi.com/s
```
`-k` and `--insecure` are the same flag.

#### Python

  ```Python
import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

proxy_pass = "testpassword_cache-all"
proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000"

proxies = {
  "https": proxy_url,
}
response = requests.get("https://ip.evomi.com/s", proxies=proxies, verify=False)
print(response.text)
```

#### Node.js

  ```JavaScript
// 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
```

#### Go

  ```Go
transport := &http.Transport{
    Proxy: http.ProxyURL(proxyURL),
    TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: transport}
```

#### Browsers

  ```javascript
// 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

```bash
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:

```bash
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

  ```bash
# Per command
curl --cacert evomi.crt -x http://rp.evomi.com:1000 -U testuser:testpassword_cache-all https://ip.evomi.com/s

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

#### Python

  ```Python
import requests

proxy_pass = "testpassword_cache-all"
proxy_url = f"http://testuser:{proxy_pass}@rp.evomi.com:1000"

proxies = {
  "https": proxy_url,
}

# 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.js

  ```bash
# Node reads additional CAs from this variable at startup
export NODE_EXTRA_CA_CERTS=/path/to/evomi.crt
node scraper.js
```

#### Go

  ```bash
# Go's crypto/x509 honours these on Unix systems
export SSL_CERT_FILE=/path/to/evomi.crt
go run main.go
```

#### Java

  ```bash
# 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

  ```bash
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`

#### Windows

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

# To remove
certutil -delstore "ROOT" "Evomi Proxy CA"
```

#### Debian/Ubuntu

  ```bash
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.

#### RHEL/Fedora

  ```bash
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:

```bash
# If you skipped verification
curl -k -x http://rp.evomi.com:1000 -U testuser:testpassword_cache-all https://ip.evomi.com/s

# If you trusted the CA
curl --cacert evomi.crt -x http://rp.evomi.com:1000 -U testuser:testpassword_cache-all 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 |
