proxy option. The package sends the endpoint to the browser’s --proxy-server switch and the credential inside --apostate-profile, and it looks up the exit’s country and timezone through the proxy before the browser starts.
Proxy URLs
The Python package’s GeoIP lookup cannot go through a SOCKS4 proxy, so with the Python package pass
locale and timezone yourself. Write the scheme every time. socks5h:// does not work. The Node package refuses it, and with the Python package every request fails with net::ERR_NO_SUPPORTED_PROXIES. Use socks5://, which already sends hostnames to the proxy to resolve.
Both packages also take the parts separately, with the raw username and password. The package encodes them.
Credentials in a URL
In a URL, write a/ in the username or password as %2F, a % as %25 and a space as %20. An @ or : in the password needs no escape, and an escaped one works too. To encode a password in code:
%zz. In 0.4.3 the limit counted characters, and the Python package passed a malformed escape on as written.
SOCKS5 with a credential
Stock Chromium cannot log in to a SOCKS5 proxy, and Playwright refuses to start with a SOCKS5 credential in its ownproxy option (Browser does not support socks5 proxy authentication). Apostate’s browser does the SOCKS5 username and password exchange itself. The packages put the credential in the --apostate-profile value, where the browser reads it, and give the driver only the endpoint. An HTTP proxy’s credential goes to the driver as well.
A wrong SOCKS5 credential fails every request with net::ERR_SOCKS_CONNECTION_FAILED. A proxy that does not answer fails it with net::ERR_PROXY_CONNECTION_FAILED.
Pass the proxy with the proxy option, not as --proxy-server in args. The Node package refuses --proxy-server in args, and the GeoIP lookup uses only the proxy option.
What stays out of logs
The browser keeps the credential in memory for the launch. It does not appear in NetLog, in Chromium’s error messages, inchrome://version or in --fingerprint-explain output.
The browser’s own command line and those of its child processes carry the --apostate-profile value, base64-encoded, so ps on the host shows the credential in that form. A package’s launch error can quote the driver’s launch log, which holds the command line. From 0.4.4 the packages remove the --apostate-profile value from launch errors. On 0.4.3, treat logs that contain launch errors as secret. A credential you put in --proxy-server yourself, without the packages, stays in the browser process’s command line as you wrote it.
GeoIP through the proxy
Before the browser starts, the package asks four GeoIP services where the exit is:ip-api.com, ipinfo.io, ipwho.is and ifconfig.co, in that order, over plain HTTP. The requests go through the proxy, and the proxy resolves the service names, so no lookup request or DNS query leaves the host except to the proxy. The first answer with a country and a timezone sets the persona’s locale and timezone. Locale and timezone covers the lookup, its timeout and what happens when it fails.
The lookup runs once, before launch. The browser keeps that locale and timezone until it closes.
One proxy per launch
--proxy-server is a browser switch, so every page of one launch goes through the same proxy. For a second exit, launch a second browser. Many sessions runs several at once.
Sticky and rotating exits
A rotating proxy gives each connection, or each few minutes, a new exit IP. Inside one browser session that means:- A site sees one session come from several IPs.
- The timezone and locale were set from the exit at launch, so after a rotation they can stop matching the IP.
Check the exit from a page
examples/python/proxy.py reads the proxy from APOSTATE_PROXY.
WebRTC behind a proxy
Behind one SOCKS5 proxy that relays UDP, the browser sends WebRTC’s UDP through the proxy. Behind an HTTP, HTTPS or SOCKS4 proxy, WebRTC gets no UDP at all. The two packages differ here. Behind a proxy, the Node package also passes the exit IP from GeoIP as--fingerprint-webrtc-ip and adds Chromium’s --force-webrtc-ip-handling-policy=disable_non_proxied_udp, and the Python package passes neither. WebRTC has the details and a leak check.