0063: Browser proxies come from the child environment
Status: accepted (2026-08-09) · Scope: toolkit tier (lib/browser, browse CLI)
Context
Section titled “Context”An embedding host can run a command with HTTP_PROXY and HTTPS_PROXY, but
Chromium does not automatically turn those variables into a Playwright browser
proxy. A test through an authenticated ISP endpoint reached the proxy, received
407 Proxy Authentication Required, and timed out because the browser had no
credential channel. A normal unproxied browse call still succeeded. The child
environment therefore prevented direct egress but could not produce a usable
proxied browser session.
An extension-based workaround was also unsuitable. Playwright’s Chromium defaults disable extensions, a clean persistent profile has no extension to configure, and installing one would make browser requests before the intended route protected the profile. Extension state also cannot provide a hard egress gate.
Decision
Section titled “Decision”Add an explicit --proxy-from-env flag to harn browse.
- Read
HTTPS_PROXY,https_proxy,HTTP_PROXY, thenhttp_proxy. - Accept authenticated
http://andhttps://URLs containing only scheme, host, port, and optional userinfo. - Remove userinfo from the server URL and pass
server,username, andpasswordto Playwright’slaunchPersistentContextproxy option. - Add Chromium’s
--force-webrtc-ip-handling-policy=disable_non_proxied_udpswitch whenever the proxy is enabled. - If the host supplies
HARNERY_BROWSER_PROXY_EXPECTED_IPandHARNERY_BROWSER_PROXY_CHECK_URL, navigate to that HTTPS checker first and require an exact IP match before opening the requested target. - Add
--export-cookies <path>for a mode-0600Netscape cookie export from the live persistent context. This is independent of the shared Harnery jar. - Add
--login-close-file <path>so an agent can keep the headed process yielded, drive the visible window through a host desktop-control capability, then trigger a clean close from a second shell call.
The flag is opt-in. Existing browse behavior does not change merely because a
developer shell happens to contain a proxy variable.
Alternatives considered
Section titled “Alternatives considered”- Keep using a Chrome extension. Rejected because the extension is absent from clean profiles, disabled by the automation browser defaults, and cannot protect its own installation request.
- Put
--proxy-server, username, and password on the command line. Rejected because command arguments are recorded by shells and agent tooling. - Automatically honor proxy environment variables. Rejected because a developer or CI environment can carry unrelated proxy settings. Browser routing must remain explicit.
- Build a new browser-control daemon. Rejected for this change. A headed
--loginprocess can stay alive while the host’s desktop-control capability drives the visible window, and a local signal file provides bounded cleanup. A new transport would add lifecycle and security surface without improving proxy enforcement. - Export the shared cookie jar. Rejected for isolated profiles launched
with
--no-cookies; those sessions intentionally never enter that jar.
Result and gotchas
Section titled “Result and gotchas”- Authenticated proxy credentials remain in the child environment and are not echoed or embedded in the Playwright server URL.
- A fixed-route host can perform both an external
curlprobe and an in-browser exact-IP gate. The target never opens when either check fails. - Native Playwright proxy routing fails closed if the proxy disappears. There is no extension toggle or direct-route fallback.
- The expected-IP variables are a pair. Supplying only one is a configuration error.
- The Netscape export contains authentication cookies and is always rewritten with owner-only permissions. Callers remain responsible for limiting its lifetime and distribution.