feat(browse): BROWSE_NO_PROXY env var to bypass system proxy on launch#1037
feat(browse): BROWSE_NO_PROXY env var to bypass system proxy on launch#1037samque1983 wants to merge 2 commits intogarrytan:mainfrom
Conversation
On macOS, Chromium (via Playwright) inherits the system HTTP/S proxy by default. When the user runs a local VPN/proxy app (Shadowrocket, ClashX, Surge, etc.) that does TLS interception, Chromium's HTTPS handshake breaks with SSL error 1 / net_error -100, and every goto fails with ERR_ABORTED. The renderer then crashes and kills the browse server, triggering the familiar '[browse] Starting server...' restart loop. curl is unaffected because curl ignores the system proxy by default, so curl-based health checks continue to pass while browse is broken — making this failure mode especially confusing. Opt-in: set BROWSE_NO_PROXY=1 in the environment and browse will pass '--proxy-server=direct://' to Chromium at launch, bypassing the system proxy entirely. Default behavior unchanged (still inherits system proxy) so corporate proxy users are not affected. Repro: # With Shadowrocket running in HTTP-proxy mode on macOS: browse goto https://example.com # ERR_ABORTED / Timeout 15000ms BROWSE_NO_PROXY=1 browse goto https://example.com # 200 OK
When a transparent-proxy VPN (Shadowrocket, ClashX, Surge in "enhanced/TUN
mode") captures traffic at the routing layer via a utun interface, the
original opt-in patch (BROWSE_NO_PROXY=1 → --proxy-server=direct://)
becomes counter-productive: direct:// can't escape TUN, packets still get
MITM'd, and Chromium's own cert store rejects the VPN's MITM cert
resulting in SSL error code 1, net_error -100 on every HTTPS navigation.
The fix in HTTP-proxy-only mode (original scenario) and the fix in TUN
mode need opposite Chromium configs:
- HTTP-proxy-only: --proxy-server=direct:// bypasses the proxy.
- TUN mode: no flag — Chromium inherits system proxy, connects through
the VPN's HTTPS proxy explicitly, and accepts the MITM cert signed
by a keychain-trusted root.
Heuristic: look for a utun interface with an IP in 198.18.x.x (IANA
benchmark range, used by Shadowrocket/ClashX for TUN routing). When
found, skip direct:// and log an explanation so the user knows why.
Backward compatible:
- BROWSE_NO_PROXY unset (default): no change, no flag added.
- BROWSE_NO_PROXY=1 + no TUN: no change, direct:// applied as before.
- BROWSE_NO_PROXY=1 + TUN: NEW — auto-skip with informative log.
- BROWSE_NO_PROXY=force: always apply direct:// (escape hatch for
users who want the old strict opt-in behavior).
Verified on macOS with Shadowrocket TUN mode active (utun6 198.18.0.1):
before patch → SSL handshake failed / net_error -100 on all HTTPS.
After patch → example.com + authenticated fly.dev pages load correctly.
|
Pushed a follow-up commit (e695f7f) that auto-detects VPN TUN mode so Problem: Shadowrocket/ClashX/Surge have two modes:
Users who set The patch: when Backward compat:
Verified 2026-04-19 on macOS with Shadowrocket TUN mode active ( Happy to split this into a separate PR if it's easier to review — let me know what you prefer. |
Problem
On macOS, Chromium (via Playwright) inherits the system HTTP/S proxy by default. When the user runs a local VPN/proxy app that does TLS interception (Shadowrocket, ClashX, Surge, V2rayU, etc.), Chromium's HTTPS handshake breaks:
Every
browse gotothen returnsERR_ABORTED, the renderer crashes, and the browse server exits — triggering the familiar[browse] Starting server...restart loop on every subsequent command.This is particularly confusing because
curlcontinues to work fine (curl ignores the system proxy by default), so health checks and CI probes pass whilebrowseis completely broken.Repro
Verified locally:
chrome-headless-shell --proxy-server="http://127.0.0.1:1082" --dump-dom https://example.comreproduces the SSL error; adding--proxy-server="direct://"fixes it.Solution
Opt-in env var
BROWSE_NO_PROXY=1passes--proxy-server=direct://to Chromium at launch, bypassing the system proxy entirely.BROWSE_EXTENSIONS_DIR.Test plan
browse goto https://example.com→Timeout 15000ms / ERR_ABORTEDBROWSE_NO_PROXY=1 browse goto https://example.com→ 200, snapshot workslaunchArgsstays empty when env var unset, so corporate proxy flow is untouched