🎉 We just launched long anticipated residential proxies & pay-per-GB!
Pay once, switch between multiple proxy providers.
50% discount for a limited time. See more →
Documentation menu

HTTPS proxy endpoint

Every Litport HTTP proxy endpoint also accepts TLS on the same host and port. Change the scheme in the proxy URL from http:// to https:// and the connection between your client and the hub is encrypted. Nothing else changes: same credentials, same port, same targets, same error codes.

Scheme Client to hub Example
http:// Plaintext http://USER:[email protected]:PORT
https:// TLS https://USER:[email protected]:PORT

PORT is the HTTP proxy port shown for your token in the dashboard (1337 and 31337 are the defaults; see Proxy ingress). SOCKS5 endpoints have no TLS variant.

Choose a security level#

There are three ways to talk to the proxy. Pick the one that matches where your client runs; the rest of this page shows how to set each one up.

Level Proxy URL and setup Passive observer (Wi-Fi, ISP, shared network) Active attacker (redirects your connection) Client support
0. Plaintext http://, nothing else Reads your credentials, every CONNECT hostname, and plain-HTTP bodies Same Every client
1. Encrypted, unverified https:// plus the client's "skip proxy verification" option Sees nothing Can impersonate the hub and capture your credentials Most libraries; not browsers, not Requests without also unverifying the target
2. Encrypted and verified (Recommended) https:// plus the Litport root file Sees nothing Fails closed: the client refuses anything that is not a Litport hub Every client that supports an https:// proxy

Recommendation: use level 2. It costs one public file and one option, and it is the only level that protects a spendable credential against both kinds of attacker. Download the root once and keep it next to your code:

curl --fail --remote-name https://litport.net/certificates/litport-cache-root.crt

Level 1 is a reasonable step up from plaintext for a quick manual test or a client you cannot give a file to. Level 0 is the default only because it is what every client supports; move off it wherever the network between you and the hub is not yours.

Two different things called "HTTPS proxy"#

  • Proxying an HTTPS target is what an HTTP proxy has always done: your client sends CONNECT example.com:443, the hub opens a tunnel, and your TLS session runs end to end inside it. The http:// proxy endpoint supports this; the tunnel contents are opaque to Litport unless you opt into cache mode.
  • An HTTPS proxy endpoint encrypts the hop between your client and the hub itself. Everything you send to the proxy, including the CONNECT line and your credentials, travels inside that TLS session.

This page is about the second one. The two are independent: you can use either scheme for any target.

What the plaintext endpoint exposes (level 0)#

With http://, anything on the path between your client and the hub can read:

Exposed on the wire Consequence
Proxy-Authorization header Your proxy username and password, on every request. A pay-per-GB credential is spendable by whoever captures it.
CONNECT host:port lines Every hostname you connect to, even when the tunnel itself is encrypted.
Plain http:// target requests Full request and response bodies.

"On the path" includes the local network you are on, your ISP, a shared cloud network, a corporate egress proxy, and any hop that logs traffic. The target site sees the same thing in both cases: the selected exit IP, never yours.

When to use https://#

Use the HTTPS endpoint when:

  • The client is on a network you do not control. Laptops on public Wi-Fi, shared offices, hotel and airport networks, and any environment with a transparent inspection device.
  • The client runs on shared or third-party infrastructure. CI runners, serverless functions, shared cloud VPCs, and containers on hosts you do not administer. Credential capture there is quiet and durable.
  • The destination list itself is sensitive. CONNECT lines reveal which hosts you research even when the content is encrypted.
  • You send plain http:// targets. Those bodies are otherwise readable end to end on the first hop.
  • Plain proxy traffic is blocked or throttled. Some networks flag CONNECT on non-standard ports; TLS to a hostname looks like ordinary HTTPS.

Stay on http:// when:

  • The client and hub already share a private, encrypted path, for example a VPN into a network you control, and you want the smallest possible per-connection overhead.
  • The client library cannot negotiate TLS to a proxy at all (see the client notes below).
  • You open very many short connections and the extra handshake per connection matters more than the exposure.

What it costs#

  • One extra TLS handshake per connection. Keep-alive clients pay it once per connection, not per request.
  • TLS overhead counts as traffic. Record framing and the handshake are bytes through the hub, so ordinary (non-cache) requests are metered slightly higher, typically a few percent on small requests and negligible on large transfers. Cache-mode accounting is unchanged.
  • Your client must trust the Litport root for the proxy connection. That is the subject of the next section, and it is the reason curl -k does not help.

The hub certificate and why -k does not work#

Level 2 means verifying the hub's certificate. The hub presents a certificate for its own hostname (for example hub-eu-1.litport.net) issued by the Litport cache root. That root is not in operating-system trust stores, so a client that verifies the proxy certificate against its default bundle fails:

curl: (60) SSL certificate problem: unable to get local issuer certificate

curl -k does not fix this. Clients keep two separate TLS legs with separate settings:

TLS leg What it protects curl options
Client to proxy Your credentials and CONNECT lines --proxy-cacert, --proxy-insecure
Client to target (inside the tunnel) The site you are fetching --cacert, -k / --insecure

-k only relaxes the target leg. The proxy leg still verifies against the system bundle and aborts. Point the proxy leg at the Litport root instead:

curl --fail --remote-name https://litport.net/certificates/litport-cache-root.crt
curl --proxy-cacert litport-cache-root.crt \
  -x 'https://USER:[email protected]:PORT' \
  'https://example.com/'

Two things to know before you install that root more broadly:

  • Trusting the root for the proxy hop does not enable cache mode. Cache mode needs a _cache- password suffix on the credential; without it the tunnel stays opaque.
  • The same root can also issue certificates for cache-mode interception. If you would rather not add it to a system-wide store, scope it to the process or container that uses the proxy, as every example below does.

Always use the hub hostname, never its IP address. The certificate is issued for the hostname.

Level 2: verified client examples#

Replace USER, PASSWORD, and PORT; percent-encode reserved characters in credentials. All examples verify the proxy certificate against litport-cache-root.crt and keep normal public-CA verification for the target. Each section ends with the level 1 switch that skips verification of the proxy leg only; read Level 1 for what that costs before using it.

curl#

Verified (recommended):

curl --proxy-cacert litport-cache-root.crt \
  -x 'https://hub-eu-1.litport.net:PORT' --proxy-user 'USER:PASSWORD' \
  'https://example.com/'

Add --verbose to see two handshakes: the first against the proxy (hub-eu-1.litport.net), then CONNECT inside it, then the target's.

Unverified, for a throwaway test only (credentials are still encrypted, but the client cannot tell a real hub from an impostor):

curl --proxy-insecure -x 'https://USER:[email protected]:PORT' 'https://example.com/'

--proxy-cacert needs curl 7.52 or later, which every current distribution ships.

Python Requests#

Requests uses one CA bundle for both legs, and verify= replaces its default bundle. Build a combined bundle once so the target keeps public-CA verification:

cat "$(python -c 'import certifi; print(certifi.where())')" litport-cache-root.crt > litport-bundle.pem
import requests

proxy = 'https://USER:[email protected]:PORT'
response = requests.get(
    'https://example.com/',
    proxies={'http': proxy, 'https': proxy},
    verify='litport-bundle.pem',
    timeout=30,
)

List the proxy under both http and https keys; Requests picks the entry by the target's scheme. TLS-in-TLS through an https:// proxy needs urllib3 1.26 or later, which Requests 2.26 and later depend on.

Requests has no proxy-only switch: verify=False disables verification of both legs, the hub and the target, and prints a warning on every request. Use it only for throwaway tests.

response = requests.get('https://example.com/', proxies={'http': proxy, 'https': proxy}, verify=False, timeout=30)

Python httpx#

httpx separates the proxy's TLS settings from the target's:

import ssl
import httpx

context = ssl.create_default_context()
context.load_verify_locations(cafile='litport-cache-root.crt')
proxy = httpx.Proxy('https://USER:[email protected]:PORT', ssl_context=context)
with httpx.Client(proxy=proxy, timeout=30) as client:
    response = client.get('https://example.com/')

The ssl_context on Proxy verifies only the hub; the client's default context still verifies the target. To skip the proxy leg while keeping the target verified, give Proxy a context with verification turned off:

insecure = ssl.create_default_context()
insecure.check_hostname = False
insecure.verify_mode = ssl.CERT_NONE
proxy = httpx.Proxy('https://USER:[email protected]:PORT', ssl_context=insecure)

Node#

https-proxy-agent accepts an https:// proxy URL and verifies the hub with Node's trust store, so append the root through NODE_EXTRA_CA_CERTS. It does not disable verification anywhere.

npm install https-proxy-agent
NODE_EXTRA_CA_CERTS=./litport-cache-root.crt node fetch.mjs
import https from 'node:https'
import { HttpsProxyAgent } from 'https-proxy-agent'

const agent = new HttpsProxyAgent('https://USER:[email protected]:PORT')
https.get('https://example.com/', { agent }, response => response.resume())

With undici (also the engine behind global fetch in Node 18 and later) the proxy leg has its own TLS options, so no environment variable is needed:

import { readFileSync } from 'node:fs'
import { ProxyAgent, fetch } from 'undici'

const dispatcher = new ProxyAgent({
  uri: 'https://hub-eu-1.litport.net:PORT',
  token: 'Basic ' + Buffer.from('USER:PASSWORD').toString('base64'),
  proxyTls: { ca: [readFileSync('litport-cache-root.crt')] },
})
const response = await fetch('https://example.com/', { dispatcher })

Both libraries can skip the proxy leg alone. For https-proxy-agent, the second argument is passed to the TLS connection to the hub; for undici it is proxyTls. Target verification stays on. Do not use NODE_TLS_REJECT_UNAUTHORIZED=0, which disables verification everywhere in the process.

const agent = new HttpsProxyAgent('https://USER:[email protected]:PORT', { rejectUnauthorized: false })
const dispatcher = new ProxyAgent({ uri: 'https://hub-eu-1.litport.net:PORT', token, proxyTls: { rejectUnauthorized: false } })

Go#

Go's http.Transport dials an https:// proxy with the same TLSClientConfig it uses for targets, so append the root to the system pool rather than replacing it:

pool, err := x509.SystemCertPool()
if err != nil || pool == nil { pool = x509.NewCertPool() }
pem, err := os.ReadFile("litport-cache-root.crt")
if err != nil || !pool.AppendCertsFromPEM(pem) { log.Fatal("could not add Litport root") }
proxyURL, err := url.Parse("https://USER:[email protected]:PORT")
if err != nil { log.Fatal(err) }
client := &http.Client{Transport: &http.Transport{
  Proxy: http.ProxyURL(proxyURL),
  TLSClientConfig: &tls.Config{RootCAs: pool, MinVersion: tls.VersionTLS12},
}}
response, err := client.Get("https://example.com/")

Go applies TLSClientConfig to both legs, so InsecureSkipVerify: true would also stop verifying the target. There is no proxy-only switch; keep the root in the pool instead.

PHP#

The curl extension exposes the same proxy-leg options as the command line:

$ch = curl_init('https://example.com/');
curl_setopt_array($ch, [
    CURLOPT_PROXY => 'https://hub-eu-1.litport.net:PORT',
    CURLOPT_PROXYUSERPWD => 'USER:PASSWORD',
    CURLOPT_PROXY_CAINFO => __DIR__ . '/litport-cache-root.crt',
    CURLOPT_RETURNTRANSFER => true,
]);
$body = curl_exec($ch);

To skip the proxy leg only, replace CURLOPT_PROXY_CAINFO with CURLOPT_PROXY_SSL_VERIFYPEER => false and CURLOPT_PROXY_SSL_VERIFYHOST => 0; the target's CURLOPT_SSL_VERIFYPEER stays at its default.

Java#

The JDK's built-in java.net.http.HttpClient only supports plaintext proxies. Use Apache HttpClient 5, which accepts an https proxy host, and give the JVM a truststore that contains the Litport root alongside the default roots:

keytool -importcert -noprompt -alias litport-cache -file litport-cache-root.crt \
  -keystore ./litport-truststore.p12 -storetype PKCS12 -storepass changeit
java -Djavax.net.ssl.trustStore=./litport-truststore.p12 \
  -Djavax.net.ssl.trustStoreType=PKCS12 -Djavax.net.ssl.trustStorePassword=changeit App
HttpHost proxy = new HttpHost("https", "hub-eu-1.litport.net", PORT);
BasicCredentialsProvider credentials = new BasicCredentialsProvider();
credentials.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials("USER", "PASSWORD".toCharArray()));
try (CloseableHttpClient client = HttpClients.custom()
        .setProxy(proxy)
        .setDefaultCredentialsProvider(credentials)
        .build()) {
    client.execute(new HttpGet("https://example.com/"), response -> EntityUtils.toString(response.getEntity()));
}

A dedicated truststore replaces the JVM default bundle, so import the public roots you need into it as well, or import the Litport root into a copy of the JVM's cacerts. Java has no proxy-only switch: a trust-all TrustManager would also stop verifying targets, so use the truststore.

Browsers and browser automation#

Browsers verify the proxy certificate with the operating-system store (Firefox with its own store). Install the root there for the profile you use, then point the browser at the HTTPS proxy:

  • Chrome and Edge: start with --proxy-server="https://hub-eu-1.litport.net:PORT", or use a PAC file returning HTTPS hub-eu-1.litport.net:PORT. The browser prompts for the proxy credentials.
  • Firefox: use a PAC file with the same HTTPS host:port directive, or set the proxy type to HTTPS in the connection settings.
  • Playwright and Puppeteer: pass server: 'https://hub-eu-1.litport.net:PORT' with the username and password. The browser still needs the root in its store; ignoreHTTPSErrors and --ignore-certificate-errors affect only target certificates. Browsers never skip verification of a proxy certificate, so installing the root is the only option.

Instructions for each operating-system and Firefox store are on the certificate page.

Clients without TLS-to-proxy support#

Scrapy's default downloader, the JDK HttpClient, and many older HTTP libraries can only talk to a plaintext proxy. For those, either keep http:// or run a small local forwarder on 127.0.0.1 that accepts plaintext and connects to the hub over TLS; the sensitive hop then stays on your machine.

Level 1: encrypted without verification#

Every verified example above has an unverified variant. Skipping verification keeps the hop encrypted, so a passive observer on the network still sees nothing. It removes the guarantee that you are talking to a Litport hub: an active attacker who can redirect your connection could present any certificate and capture your credentials. That is the same exposure level 0 has against an active attacker, so treat level 1 as a step up from http://, not as equivalent to level 2.

Client Proxy leg only Both legs
curl --proxy-insecure --proxy-insecure -k
Python Requests not available verify=False
Python httpx Proxy(..., ssl_context=insecure) add verify=False on the client
Node https-proxy-agent { rejectUnauthorized: false } agent option NODE_TLS_REJECT_UNAUTHORIZED=0 (avoid)
Node undici proxyTls: { rejectUnauthorized: false } add requestTls: { rejectUnauthorized: false }
Go not available InsecureSkipVerify: true
PHP curl CURLOPT_PROXY_SSL_VERIFYPEER / CURLOPT_PROXY_SSL_VERIFYHOST add CURLOPT_SSL_VERIFYPEER => false
Java not available trust-all TrustManager (avoid)
Browsers not available not available; install the root

Prefer level 2 for anything that runs unattended. Verification costs one file and one option; the credential it protects is spendable.

Checking that TLS is really in use#

  • curl --verbose prints the proxy handshake before the CONNECT line, including the certificate subject CN=hub-eu-1.litport.net.
  • A wrong password over https:// still returns 407 with X-Proxy-Error-Code: 4, exactly as over http://; the error headers travel inside TLS.
  • If the connection closes immediately with no HTTP response, the hub could not complete the TLS handshake. Check that you used the hostname rather than an IP address, that the scheme is https:// on an HTTP port and not a SOCKS5 port, and that your client trusts the Litport root for the proxy leg. See Proxy error reference.