A proxy that works for HTTP but not HTTPS is accepting one request and refusing another. Ask it for http://example.com/ and the client sends the whole URL, which the proxy fetches and hands back. Ask for https://example.com/ and the client sends CONNECT example.com:443 instead, wants a tunnel, and only then starts TLS with the site. Those are two code paths in the proxy and often two permissions in the seller's product, and since nearly every page is HTTPS, a gateway that refuses the second one leaves a profile with almost nothing to load.
Most proxy checks only send the first request. Ours sends both, for that reason.
Why a proxy works for HTTP but not HTTPS
RFC 9110 defines both. A plain request through a proxy uses the absolute form, so the request line names the full address. Ours looks like this:
GET http://ip-api.com/json HTTP/1.1
Host: ip-api.com
The proxy opens its own connection to ip-api.com, reads the JSON and relays it. It sees the page and could rewrite it if it wanted to.
For HTTPS that would defeat the point of the padlock, so the client asks for a pipe:
CONNECT www.cloudflare.com:443 HTTP/1.1
Host: www.cloudflare.com:443
A 200 back means the pipe is open. After that the proxy copies bytes both ways without reading them: the handshake, Cloudflare's certificate and the page all pass through encrypted. RFC 9110 also tells proxies to limit CONNECT to a known set of ports, which is why so many allow 443 and refuse everything else.
So the failure has a few common shapes. A seller's HTTP-only plan or port answers GET and refuses CONNECT. A port meant for another product answers CONNECT with 403 or 405. And an address with the wrong port can still return a page of its own to a plain GET, which looks like success.
Paste one gateway line. Every profile mints its own exitIn Mosaic: Provider minting. One gateway line, a separate exit for every profile. Free holds 3 profiles. Pro is $49 a month for any number.
Mosaic is a desktop app
Email yourself the link and open it on your computer.
How it works: Provider minting
Why the usual proxy check passes
A typical checker sends a GET through the proxy to an IP lookup page, and many lookup pages are plain HTTP. The proxy fetches it, the page prints an address in the right country, and the checker shows green. CONNECT was never tried.
Chrome tries it on the first page it opens. A refused CONNECT becomes ERR_TUNNEL_CONNECTION_FAILED, Chrome throws away the proxy's own reply along with whatever reason it gave, and the person at the keyboard gets a grey error page from a proxy whose dashboard says it is fine.
How to test both paths with curl
Send the same proxy one http:// URL and one https:// URL, with -v so curl prints what it sends and receives:
curl -v -x http://user:pass@host:port http://example.com/
curl -v -x http://user:pass@host:port https://example.com/
In the second run, find the reply under CONNECT example.com:443. HTTP/1.1 200 is an open tunnel, and anything after it is between you and the site. A 403, 405, 407 or 502 at that line came from the proxy, and no TLS was sent at all.
The opposite mistake looks the same from the browser. The scheme on a proxy address describes the hop to the proxy, so HTTPS_PROXY=http://host:port is normally right for an ordinary HTTP proxy, and https://host:port makes curl start a TLS handshake with a proxy that never speaks TLS. A tool with a separate HTTPS proxy setting left blank does something else again: it sends HTTPS traffic straight out, so the proxy seems ignored rather than broken.
How Mosaic checks a proxy, in two separate requests
Our check runs through the same local relay a profile launches with, with the same credentials and the same session token:
- A plain HTTP GET to
ip-api.com, which returns the exit's address, country, ISP and timezone. - A CONNECT to
www.cloudflare.com:443, a TLS handshake through the tunnel, and a GET for/cdn-cgi/trace. Cloudflare answers in a few lines of plain text, among themip=andloc=: the address and country it saw.
We write the second request by hand on a raw socket, because a library would pick its own route and we want CONNECT, then TLS, the way Chrome does it. The first version of that probe reported every proxy as a failed tunnel. One set of socket handlers did both steps, and its close listener fired when TLS took the socket over, which raced the real answer and won. Now the CONNECT happens on the raw socket, the socket goes to TLS, and the probe stops listening to it.
A refused tunnel gets its own sentence, with the gateway's status in the brackets: "The proxy answered plain HTTP but refused CONNECT (403). Profiles behind it will not load HTTPS sites." The row in the proxy list reads No HTTPS. The check runs up to three attempts when a residential pool hands out a dead exit, but a refused CONNECT is on its short list of answers that are final, so it is not retried. Start then refuses to open a profile behind that proxy: the message says it "answered but refused to tunnel, so this profile was not started", and names the wrong port or a plan without HTTPS as the usual cause.

Two requests also mean two readings of the exit. On one endpoint we tested, three requests in a row came back from Peru, Brazil and Bahrain, so different addresses from the same family mark the proxy as rotating. A residential exit once showed 172.115.91.44 to ip-api.com and an IPv6 address to Cloudflare on three checks in a row, and the first version called that rotation too. Now an IPv4 reading next to an IPv6 one counts as no evidence either way (why one exit can show two addresses). The country, the reputation and the other things to read before trusting a proxy with an account are in how to check a proxy before you use it.






