Why WSL2 Ignores the Windows System Proxy
When you toggle system proxy in a graphical Clash client on Windows, you are mostly influencing WinINET-style settings that cooperative desktop apps read. WSL2 is not a thin compatibility layer anymore; it runs a real Linux kernel in a lightweight utility VM with its own routing table and resolver behavior. Nothing in that VM automatically mirrors whatever Edge or Chrome picked up from the host, so curl, apt, git, and npm default to direct egress unless you teach them otherwise.
The practical consequence is familiar on forums: “Browser works, terminal does not.” That sentence is not magic; it is two different network stacks. Your job is to bridge them by targeting the proxy port your Meta-class core listens on—typically a mixed HTTP and SOCKS listener—using an IP address that the Linux side can actually route to. Guessing 127.0.0.1 inside WSL2 usually points at the Linux loopback, not the Windows service you want, which is why people reach for the host IP instead.
If you have not installed or stabilized the Windows client yet, start with our Clash Verge Rev Windows setup guide so mixed ports, profiles, and logs are trustworthy before you debug cross-VM addressing.
Find the Windows Host IP from Inside WSL2
Modern WSL2 builds expose the Windows host through a predictable default gateway on the virtual Ethernet adapter. A common pattern is to read the gateway of the default route:
ip route show | grep -i default | awk '{ print $3 }'
Another popular approach is to parse /etc/resolv.conf for the nameserver line, because Microsoft often points DNS resolution at the host. Both methods can drift if you customize networking, so when something breaks after an update, re-validate the address instead of assuming an old note from six months ago is still true.
Store the result in a shell variable for the session, for example export WIN_HOST=$(ip route show | grep -i default | awk '{ print $3 }'), then build proxy URLs with it. If you script this for teams, document which command you standardized so onboarding stays boring and reproducible.
Make Sure Clash Listens Where WSL2 Can Reach
Even with the correct host IP, you will see silent failures if the proxy only binds to 127.0.0.1 on Windows. That bind address is loopback on the host OS; traffic originating from the WSL2 VM arrives on a different interface and never hits a localhost-only socket. Many templates therefore expose the mixed port on 0.0.0.0 or an explicit LAN-facing address. Align what the YAML or GUI says with what netstat or PowerShell shows in practice.
Treat this as a security decision: opening the listener beyond loopback increases exposure on coffee-shop Wi-Fi. Pair broad binds with firewall rules you understand, or restrict to the virtual switch used by WSL2 if your threat model demands it. The engineering tradeoff is always reachability versus attack surface.
Firewall: Windows Defender Firewall may block inbound connections to the Clash port from the WSL virtual NIC until you allow the rule for your client executable.
Map HTTP, HTTPS, and SOCKS to the Mixed Port
Clash “mixed” ports accept both HTTP proxy semantics and SOCKS on the same listener in typical Meta-class setups, but your exact profile might split them. Open the client UI or config and note the numbers. A very common default is 7890 for mixed mode, yet your subscription vendor might ship something else entirely.
When you export environment variables, match scheme to capability. HTTP and HTTPS variables should point at an HTTP proxy URL if that is what the port speaks; SOCKS-aware tools want socks5:// endpoints. If you mix these up, you get confusing partial success—git might work while npm throws ECONNRESET because one stack insists on CONNECT tunneling and the other expects SOCKS handshake bytes.
Shell Environment Variables for Terminals
For interactive shells, set lowercase and uppercase variants because different tools consult different spellings across decades of Unix heritage:
export WIN_HOST=$(ip route show | grep -i default | awk '{ print $3 }')
export http_proxy="http://${WIN_HOST}:7890"
export https_proxy="http://${WIN_HOST}:7890"
export all_proxy="socks5://${WIN_HOST}:7890"
export HTTP_PROXY="$http_proxy"
export HTTPS_PROXY="$https_proxy"
export ALL_PROXY="$all_proxy"
Replace 7890 with your real mixed port. If your node only exposes separate HTTP and SOCKS ports, split the URLs accordingly instead of forcing everything through one number that does not exist.
Persist these lines in ~/.bashrc or ~/.zshrc if you want every new tab to inherit the policy, but consider gating them behind a function like proxy_on / proxy_off so corporate VPN days do not inherit stale exports by accident.
NO_PROXY: Keep Localhost and LAN Traffic Direct
Blindly sending every hostname through a proxy breaks local development: package managers talking to a registry mirror on your LAN, containers bound to bridge networks, or services listening on 127.0.0.1 inside Linux. Maintain a deliberate bypass list:
export no_proxy="localhost,127.0.0.1,::1,.local"
export NO_PROXY="$no_proxy"
Expand the list with internal domain suffixes your team actually uses. The goal is not minimal typing; the goal is fewer mystery stalls when npm tries to reach a private Verdaccio instance and your proxy node has no route to it.
Configure Git Inside WSL2
Git respects http.proxy and https.proxy for HTTP-based remotes, and can use http.proxy style settings for some HTTPS flows depending on build and remote URL. A straightforward global pattern:
git config --global http.proxy "http://${WIN_HOST}:7890"
git config --global https.proxy "http://${WIN_HOST}:7890"
For SSH remotes such as [email protected]:..., these keys do nothing; SSH needs ~/.ssh/config ProxyCommand with something like nc or connect-proxy through SOCKS, or you must switch the remote to HTTPS. Pick one story per repository and document it in your team handbook so newcomers do not flip URLs randomly.
When you finish a session that must be direct, unset with git config --global --unset http.proxy (and the HTTPS counterpart) or use conditional includes in newer Git versions to apply proxy only on work laptops.
npm, yarn, pnpm, and Registry Mirrors
npm historically honored HTTP_PROXY environment variables, but many teams still set explicit config for clarity:
npm config set proxy "http://${WIN_HOST}:7890"
npm config set https-proxy "http://${WIN_HOST}:7890"
Corporate registries and mirrors complicate the picture. If you point npm at an internal mirror, you might want that traffic DIRECT while GitHub tarball fetches still use the proxy—this is policy, not a single toggle. Our Clash routing guide for GitHub and npm walks the split-routing mindset that pairs well with this networking plumbing.
yarn classic and pnpm generally follow the same proxy environment variables; verify with a verbose install once after changes. Cache directories and offline modes can mask misconfiguration until you delete node_modules and retry.
apt, curl, wget, and Language Package Managers
curl and wget pick up http_proxy / https_proxy in most builds, which makes them excellent smoke tests: if a simple curl -I https://example.com still hangs, fix routing before blaming npm. For apt, some distributions honor /etc/apt/apt.conf.d/ Acquire::http::Proxy directives; others expect environment exports in a systemd drop-in for apt—check your distro docs instead of cargo-culting a single file path from 2019 blog posts.
Python pip, Rust cargo, and Go modules each have their own proxy environment variable names. The pattern is always the same: prove host reachability first, then map the tool to the variable it actually reads.
Localhost Forwarding and Mirrored Networking
Sometimes you need a Linux process to talk to a server bound to 127.0.0.1 on Windows, or the reverse. Newer WSL2 releases and .wslconfig options such as localhostForwarding influence whether localhost in the guest reaches the host loopback. When documentation says “open http://localhost:port from WSL2,” test it explicitly with curl; if it fails, use the host IP plus port instead of fighting ambiguous localhost semantics across two kernels.
Windows 11 has been experimenting with networking modes such as mirrored adapters that change how guests see the host. After major OS upgrades, rerun your IP discovery commands; treat networking like code that regresses across releases.
When Windows TUN Is Simpler Than Per-Tool Proxy
If you need every Linux socket captured without maintaining exports for fifteen tools, consider enabling TUN mode on the Windows side with Service installation so the host stack intercepts broadly, then let WSL2 traffic traverse the virtual switch according to your routes. This is not automatically “better,” but it reduces the whack-a-mole of forgotten environment variables. Follow the Clash Verge Rev TUN mode guide for Service prerequisites and DNS alignment—skipping Service install is a classic reason people think TUN “does nothing.”
Verify in Clash Logs and With Simple Tests
Open the Connections pane while you run curl, git ls-remote, or npm ping. You should see flows whose source path corresponds to the WSL2 virtual network, not only browser processes. If nothing appears, packets never reached the proxy listener—return to bind addresses and firewall rules. If flows appear but hit DIRECT unexpectedly, your rule set needs attention; see Meta core DNS leak prevention when symptoms look like resolver drift rather than raw connectivity.
Short Ordered Playbook
1Confirm Clash on Windows works in a browser
Establish baseline on the host before you debug the guest. If the Windows side is broken, WSL fixes will not help.
2Resolve WIN_HOST and ping it from WSL2
If ICMP is blocked, try curl to the mixed port; absence of TCP reset is informative.
3Confirm the listener is not loopback-only
Adjust bind settings or firewall, then retest with a single curl through http_proxy.
4Layer Git and npm configuration
Only after generic HTTP works should you specialize tools.
Notebook habit: Record WIN_HOST command output, mixed port number, and Clash mode after each Windows update—WSL networking regressions love to arrive silently.
Open Source, Downloads, and Trust
Meta-class cores and Verge Rev publish sources for transparency—use them to read changelogs and file precise issues. For day-to-day installers, prefer the official Clash download page; treat GitHub as the engineering home, not the only distribution channel.
Summary
WSL2 does not inherit the Windows system proxy, so Git, npm, and shells need explicit targets. Discover the host IP from the guest, ensure Clash listens on an address the VM can reach, export consistent proxy variables, carve NO_PROXY for local work, and specialize Git and npm only after generic HTTP tests succeed. Compared with chasing random YAML keywords while angry, this sequence is dull—and dull is how developers ship.
When your Windows client stays maintainable across modes and logs stay readable, the Linux side stops feeling like a second internet.