Why WSL2 Breaks the “Just Use Localhost” Intuition

WSL2 ships a real Linux kernel inside a virtualized utility VM. From inside that environment, 127.0.0.1 refers to the Linux loopback interface, not the Windows loopback where your Clash GUI or service is listening. Many first-time setups therefore look correct on paper—export https_proxy=http://127.0.0.1:7890—yet every curl times out because nothing on the Linux side is bound to that port. The mental model you want is simple: treat Windows as a separate machine on a tiny virtual Ethernet segment, discover its address dynamically, and aim your WSL2 Clash proxy traffic at that Windows host port instead of localhost.

This workflow intentionally differs from running Clash Meta directly on bare-metal Ubuntu, where systemd owns the daemon and 127.0.0.1 really is local. If you later move workloads onto native Linux, our Clash Meta on Ubuntu and systemd guide remains the authoritative path for headless installs. Inside WSL2, you are composing two stacks: the Windows listener you already configured in Clash Verge Rev on Windows (or any Meta-based Windows client) plus Linux-side environment variables that steer user-space tools outward.

💡
Design goal One outbound policy engine on Windows, zero duplicate subscriptions inside WSL, predictable ports, and logs you can read from a single Clash dashboard when apt or git misbehaves.

Discover the Windows Host IP from Inside WSL2

Microsoft documents several ways to learn the Windows host address visible to WSL2. The most portable pattern for scripting is to read the default gateway of the default route inside Linux, because WSL2 sets that gateway to the Windows side of the virtual switch:

ip route show default | awk '{print $3}'

Another common signal is the resolver file WSL generates. When automatic generation is enabled, /etc/resolv.conf often lists a nameserver line that also points at the Windows host. Treat both values as hints, not contracts: corporate VPNs, custom .wslconfig networking experiments, or future hypervisor tweaks can change behavior. For interactive shells, capture the gateway once per session into a variable such as WIN_HOST and reuse it when building http://$WIN_HOST:7890 style URLs.

If you hard-code an address in ~/.bashrc, expect breakage after major Windows updates or when switching between Wi-Fi and Ethernet. A lightweight guard is to resolve on shell startup and log the chosen IP when set -x debugging is active. For automation pipelines, prefer explicit configuration management over silent guesses.

mixed-port, bind-address, and “Allow LAN” Semantics

Clash-class cores expose HTTP and SOCKS listeners. The mixed-port key (or separate port and socks-port if you prefer classics) defines where clients connect. Defaults near 7890 are community folklore, not guarantees—your YAML or GUI export is authoritative. The crucial second axis is which interface receives inbound connections. Binding only to 127.0.0.1 on Windows means “accept loops from Windows itself,” which excludes WSL2 packets arriving through the virtual NIC. You need the listener attached to an address reachable from the WSL2 side, typically 0.0.0.0 for all IPv4 interfaces or the explicit LAN-facing address of the Windows vEthernet adapter.

GUI clients surface the same idea under names like Allow LAN or Bind mixed port on all interfaces. Turning that option on does not magically weaken your threat model by itself; it widens exposure to anything else on reachable networks, which is why pairing it with sane Windows firewall rules matters. If you intentionally share Clash with other devices on Wi-Fi, read LAN sharing for phones and consoles for a broader discussion of SSID boundaries and guest networks. WSL2 is closer to a loopback cousin than a random phone, but the listener topology is the same problem class.

After changing bind settings, verify from Windows with netstat or PowerShell Get-NetTCPConnection that the mixed listener is in Listen state on 0.0.0.0:7890 (example port) rather than 127.0.0.1:7890 only. Then verify from WSL2 with curl -v --proxy http://$WIN_HOST:7890 https://www.google.com/generate_204 or any lightweight health URL your rules allow.

Windows Defender Firewall and Hyper-V Virtual Adapters

Even correct YAML and correct environment variables fail silently when the Windows firewall drops SYN packets from WSL2 before they reach Clash. The symptom pattern is maddening: browser traffic on Windows works because it never crosses that edge, while Linux-side curl hangs at “Trying …” Open Windows Defender Firewall with Advanced Security, locate inbound rules for your Clash executable or the configured port, and ensure Private profiles at minimum permit the listener. Some users temporarily create a narrow allow rule scoped to the WSL virtual subnet for troubleshooting, then tighten source prefixes once confirmed.

Antivirus suites that inject LSP or WFP filters can add a second layer of breakage. If disabling HTTP inspection for localhost-adjacent paths fixes WSL2 but weakens scanning globally, prefer splitting rules by interface profile rather than turning security products off wholesale. Document what you changed; future you will not remember why port 7890 was special.

bash, zsh, and the Full Proxy Environment Variable Set

Most CLI tools respect lowercase http_proxy and https_proxy, while others look for uppercase variants. Cargo, Go modules, npm, and assorted SDKs may read ALL_PROXY when they want a single catch-all scheme. A defensive interactive profile therefore exports both families:

export WIN_HOST=$(ip route show default | awk '{print $3}')
export HTTP_PROXY="http://${WIN_HOST}:7890"
export HTTPS_PROXY="http://${WIN_HOST}:7890"
export ALL_PROXY="socks5h://${WIN_HOST}:7890"

The snippet above is illustrative: align the port numbers with your actual mixed listener and SOCKS port, and choose http:// versus socks5h:// based on what your Windows side really exposes. The trailing h in socks5h asks curl-like libraries to resolve DNS remotely through the proxy, which matters when WSL2 DNS paths disagree with Windows resolver behavior. When domestic domains should bypass the tunnel entirely, maintain a NO_PROXY list with comma-separated suffixes such as localhost,127.0.0.1,.corp.example.

Place exports in ~/.bashrc, ~/.zshrc, or a dedicated file sourced from both. For non-interactive scripts, remember that cron and systemd user units do not automatically load shell rc files unless you wire them explicitly. If you need kernel-level capture for binaries that ignore env vars, compare this approach with Clash TUN mode for Git, npm, and games; TUN is heavier but removes the “does this binary honor HTTPS_PROXY?” guess.

apt and https: Acquire::http::Proxy Versus Environment Variables

Debian apt historically ignores generic proxy exports unless Acquire::http::Proxy and friends are set in apt configuration. The concise pattern is a drop-in under /etc/apt/apt.conf.d/:

Acquire::http::Proxy  "http://10.255.255.254:7890/";
Acquire::https::Proxy "http://10.255.255.254:7890/";

Replace the placeholder IP with your live WIN_HOST value. Some teams script regeneration of that file whenever WSL starts to avoid drift. Others wrap apt with sudo env http_proxy=...; both work when permissions line up. Remember that apt talks to distribution mirrors over HTTP or HTTPS depending on mirror configuration; mismatched schemes in proxy URLs produce misleading TLS errors that look like “bad mirror day” instead of “bad proxy string.”

Corporate intercepting proxies chained in front of Clash add another wrinkle: apt may need a custom CA trust store inside WSL2 even when Windows already trusts the same root. Keep that trust work separate from Clash routing; routing can be perfect while TLS still fails on certificate mismatch.

git, SSH remotes, and credential helpers

For https:// Git remotes, git respects http.proxy configuration either globally or per repository. Example:

git config --global http.proxy http://10.255.255.254:7890
git config --global https.proxy http://10.255.255.254:7890

SSH-based [email protected]:org/repo.git URLs do not automatically pick up HTTP proxy variables. Users either switch remotes to HTTPS when behind strict HTTP-only corporate paths, run nc -X connect -x host:port style ProxyCommand tunnels, or rely on TUN-level capture. Pick deliberately; half-working SSH setups frustrate teams more than honest HTTPS conversions.

When git Large File Storage or submodule tooling shells out to curl, inherited environment variables matter again. If only git fails while bare curl works, compare git config --list --show-origin against your shell exports for duplicate or contradictory proxy entries.

IPv6, dual-stack mirrors, and DNS leakage tests

Some CDNs prefer IPv6 when both A and AAAA records exist. If your Windows Clash listener is IPv4-only while apt races toward an IPv6 mirror address, connections may bypass the proxy path you thought you configured. Symptom triage starts with curl -4 versus curl -6 comparisons and checking whether disabling IPv6 temporarily changes behavior. Longer-term fixes include aligning Windows and WSL2 resolver policies or extending listener coverage to IPv6 where your client supports it.

DNS over the virtual switch can also surprise newcomers: WSL2 may resolve names differently than Windows, yet your rules still classify flows based on the SNI seen after resolution. When domains flap between domestic and overseas answers, Clash logs remain the source of truth. If you have never read them, start with how to read Clash logs and rule hits before you swap DNS modes blindly.

Operational Checklist Before You Blame the Subscription

Walk this sequence when anything fails: confirm Windows Clash itself can fetch a test URL; confirm the mixed listener binds beyond localhost; confirm WIN_HOST prints a plausible gateway; confirm curl through explicit --proxy works before relying on environment auto-detection; confirm apt has either matching Acquire lines or consistent sudo env forwarding; confirm git remote scheme matches your proxy strategy; confirm firewall rules allow the WSL subnet; finally, inspect Meta logs for policy denials versus handshake timeouts.

Compared with maintaining a second Mihomo binary inside every distro tarball, reusing Windows Clash keeps subscription refresh, GUI overrides, and GeoIP updates in one place. When you want curated installers for the Windows side or parity builds for other platforms, → Download Clash for free and experience the difference between ad hoc scripts and a coherent release channel.