Why 127.0.0.1 inside WSL2 is not your Windows Clash
WSL2 runs a real Linux kernel in a lightweight utility virtual machine. Your Ubuntu shell, apt, curl, and git execute in that guest network namespace. When you export http_proxy=http://127.0.0.1:7890 inside WSL, the address refers to loopback inside the guest, not the Windows host where Clash is actually listening. A browser on Windows can talk to 127.0.0.1:7890 because the browser process shares the host stack; WSL does not magically bridge that semantic unless you use a dedicated forwarding mechanism such as localhostForwarding for specific scenarios—which you should not rely on as a substitute for a clean, explicit host IP when debugging proxies.
The fix is therefore architectural: discover the Windows host IP as seen from WSL, point your HTTP and SOCKS5 URLs at that address plus the same proxy port you already use on Windows (often the mixed-port), and align DNS so name resolution does not contradict what Clash expects. Once those three axes—IP, port, resolver—line up, most “Clash works in Edge but apt update hangs in WSL” tickets disappear.
This guide complements our walkthrough for Docker and terminal traffic via mixed-port, which assumes processes share a single loopback namespace. WSL2 is the high-traffic exception: dual stacks separated by a virtual switch. If you are also tuning Windows itself, skim Clash Verge Rev on Windows 11 for first-run GUI notes; here we stay focused on the Linux side of the glass.
Step 1: Read the Windows host IP from WSL2
Microsoft documents that the Windows host is reachable from WSL2 guests at the IP address listed as the nameserver in /etc/resolv.conf on current builds. Open the file with cat /etc/resolv.conf and copy the IPv4 after nameserver; that value is typically an address on the virtual eth0 path toward Windows, not a public WAN address. Treat it as your WIN_HOST shell variable for the rest of the session.
Some distributions ship systemd-resolved stubs that rewrite resolv.conf. If the file is a symlink or shows 127.0.0.53, fall back to routing data instead: ip route show default and read the via gateway on the default route—on many installs that gateway is still the Windows host for outbound from the guest. When both methods agree, you have high confidence; when they disagree after an Insider upgrade, trust the method that still answers ping with one hop to a host-only address.
Optional cross-check from Windows PowerShell: wsl.exe hostname -I shows the guest side, while Get-NetIPAddress on the vEthernet (WSL) adapter shows the hypervisor switch range. You do not need perfect memorization of the diagram—only a single stable IPv4 string that answers curl --connect-timeout 2 http://<IP>:<PORT> once Clash is listening for LAN access (next section).
export WIN_HOST="$(grep -m1 '^nameserver' /etc/resolv.conf | awk '{print $2}')"
echo "$WIN_HOST"
Step 2: Make Clash accept connections from the WSL NIC
If your Clash listener is bound only to 127.0.0.1 on Windows, packets sourced from the WSL virtual NIC never hit that socket. Two levers usually appear in Mihomo-class config.yaml or the GUI equivalent: allow-lan: true (or a similarly named toggle) and a bind-address that includes the interface where WSL arrives—commonly '*' or 0.0.0.0 for IPv4. After changing listeners, restart the core and confirm from Windows that something is listening on your chosen proxy port across interfaces, not only loopback.
Keep the numeric proxy port identical to what you already publish as mixed-port (or separate port and socks-port if you deliberately split schemes). Mixed mode is attractive because curl can use http:// while other tools use socks5h:// on the same TCP port—see the conceptual split in the Docker article linked above. Document the port in your team wiki; nothing is more brittle than a screenshot that still shows 7890 while someone locally bumped to 7897.
mixed-port: 7890
allow-lan: true
bind-address: '*'
allow-lan with Windows Defender Firewall allow rules scoped to the WSL virtual subnet, or limit by source address after you measure the prefix. For broader LAN sharing patterns, read LAN sharing and firewall on this site.
Step 3: Windows Defender Firewall must allow inbound TCP to Clash
Even correct YAML fails silently if the OS drops SYN packets before they reach the userspace binary. Create an inbound rule that permits TCP on your proxy port for the Clash executable, or for the private profile only if you want a narrower blast radius. After saving the rule, test from WSL with nc -vz "$WIN_HOST" 7890 or curl -v --proxy "http://$WIN_HOST:7890" https://example.com (replace example.com with a host you are allowed to probe). Timeout here almost always means firewall or bind-address, not subscription quality.
Corporate MDM sometimes resets custom rules after policy sync. If WSL breaks “every Monday,” snapshot the rule export and re-import, or move Clash behind a corporate-approved split tunneling product—this article cannot override IT constraints, but it can save you from blaming the wrong layer.
Step 4: Export HTTP and SOCKS URLs toward $WIN_HOST
Once TCP reaches the port, standard Unix environment variables do the heavy lifting. Mirror uppercase and lowercase spellings because some libraries only read one form. For HTTPS-heavy workflows, http://$WIN_HOST:7890 is usually enough when the mixed port speaks HTTP CONNECT. For tools that want remote DNS resolution through the proxy, prefer socks5h://$WIN_HOST:7890 in ALL_PROXY so libc DNS inside WSL does not fight Clash’s own fake-ip view.
export WIN_HOST="$(grep -m1 '^nameserver' /etc/resolv.conf | awk '{print $2}')"
export http_proxy="http://${WIN_HOST}:7890"
export https_proxy="http://${WIN_HOST}:7890"
export HTTP_PROXY="$http_proxy"
export HTTPS_PROXY="$https_proxy"
export all_proxy="socks5h://${WIN_HOST}:7890"
export ALL_PROXY="$all_proxy"
Persist by appending guarded blocks to ~/.bashrc or ~/.zprofile, but consider a wsl_proxy_on function that recomputes WIN_HOST each shell startup—WSL updates can rotate the internal address more often than physical LAN DHCP does. Pair exports with a thoughtful NO_PROXY list so RFC1918 Git servers, Docker registries, or corporate PyPI mirrors stay direct.
Step 5: DNS — resolv.conf, leaks, and fake-ip
Misaligned DNS is the second silent killer after localhost confusion. WSL historically auto-generated /etc/resolv.conf to point at a Windows resolver stub; if you override it incorrectly, Linux may query a resolver that never sees the domains your rules expect. When Clash runs in fake-ip mode on Windows, upstream applications still perform their own lookups unless you route them through the core’s DNS hijack path—which WSL guests do not automatically inherit just because Windows browsers did.
Practical approach: keep WSL’s resolver functional (do not point nameserver at random public IPs unless you understand the trade-off), and rely on socks5h or HTTP proxy CONNECT so sensitive tools push name resolution to the proxy hop. For deeper DNS tuning inside YAML itself, read Clash Meta DNS: nameserver, fallback, and fake-ip-filter and mirror the same logical policy on Windows before you chase guest-only symptoms.
Advanced users can manage /etc/wsl.conf with [network] and generateResolvConf = false to supply a static resolver file—only do this if you commit to maintaining it; otherwise leave Microsoft’s generator enabled and focus on proxy-side DNS consistency.
Step 6: apt, git, and language package managers
APT honors http_proxy/https_proxy for most fetchers. If you still see direct timeouts, add explicit Acquire lines in /etc/apt/apt.conf.d/95proxies so the method layer cannot ignore the environment in edge configurations. Remember Debian/Ubuntu also need ftp_proxy only if you still have legacy URIs—rare in 2026.
Git can follow the environment, but many developers prefer explicit repository settings: git config --global http.proxy http://$WIN_HOST:7890 and https.proxy similarly. That makes behavior obvious inside IDEs that spawn Git child processes without inheriting your interactive shell exports. When cloning giant monorepos, also set NO_PROXY for self-hosted remotes on private IP space.
Node, Python, and Rust tooling each have their own env vars or config files; once curl https:// succeeds through the proxy, fix tool-specific knobs only if traces still show SYNs leaving WSL without hitting Clash.
Step 7: Design NO_PROXY for dual-stack daily work
Blindly exporting globals breaks access to SMB shares, internal Artifactory hosts, or corporate SSO endpoints that must stay on split-tunnel direct paths. Add comma-separated hosts and leading-dot suffixes your libc respects: localhost,127.0.0.1,::1,.corp.example,.lan. Verify with curl -v that direct flows skip the CONNECT handshake while external probes still show proxy negotiation.
How Windows TUN mode relates (and does not fix WSL by itself)
Enabling TUN on the Windows host steers many Win32 applications transparently, but WSL2 traffic traverses the virtual switch first. Do not assume “TUN on in Clash” equals “every Linux packet magically entered the same divert path.” Many teams still keep explicit proxy env vars for WSL even when TUN is on, because it is predictable and easy to log. Treat TUN as a complementary layer, not a reason to skip the host IP exercise.
Verification checklist before you open a bug report
Run the checks in order; the first failure tells you which section to revisit:
ping -c1 "$WIN_HOST"— confirms basic L3 reachability to Windows.nc -vz "$WIN_HOST" 7890— confirms TCP acceptance on your proxy port.curl -I --max-time 15 --proxy "http://$WIN_HOST:7890" https://example.com— validates HTTP proxy path and TLS.sudo apt updateafter exports — validates high-level package fetches.git ls-remote https://github.com/...— validates Git HTTPS through the same stack.
If step 2 fails, revisit firewall and bind-address. If step 3 fails but step 2 passes, inspect Clash logs for TLS or rule drops—our timeout and TLS log guide covers how to read those lines without guessing.
Common mis-configurations that only break WSL
Stale WIN_HOST after resume: sleep/hibernate can change the internal vSwitch addressing. Recompute the variable instead of hardcoding yesterday’s IPv4 literal in dotfiles.
IPv6-only targets: if your exports use IPv4 literals but the workload prefers AAAA records, either enable dual-stack listening in Clash or force IPv4 curl with -4 during triage so you know which family misaligned.
Corporate SSL inspection: if Windows trusts a bluecoat root but WSL does not, HTTPS through the proxy may fail with certificate errors independent of node latency. Import the same trust anchor into the guest CA bundle when policy allows.
Wrap-up
Routing WSL2 traffic through Clash on the Windows host is mostly a lesson in namespaces: the guest’s localhost is not the host’s, the reachable Windows address comes from resolv.conf or the default route, and the listener must explicitly accept that path via allow-lan, bind scope, and firewall rules. Align HTTP and SOCKS5 URLs on the same proxy port you already use in Win32, keep DNS behavior honest with your fake-ip policy, and verify with curl before you blame upstream airline Wi-Fi.
Once the plumbing is boringly reliable, you can return attention to what matters—split routing, policy groups, and readable logs—instead of fighting invisible loopback traps. Compared with ad hoc port forwarding hacks, explicit host IP plus environment variables survives kernel upgrades and new distros far better.
→ Download Clash for free and experience the difference—then set mixed-port on Windows, open the firewall to WSL, export http_proxy toward $WIN_HOST, and prove apt update before you touch heavier workflows.