Git and npm Ignore Proxy in WSL2? Share Windows Clash Step-by-Step (2026)
You finally got Clash humming on Windows 11—Edge respects the system proxy, subscriptions refresh, and split rules look sane—then you open WSL2, run git clone or npm install, and the terminal acts like the proxy does not exist. That is not a moral failure; it is architecture. WSL2 is a lightweight virtual machine with its own network namespace. It does not automatically inherit WinINet “Internet Options,” nor the per-user proxy toggles your GUI client flipped. This guide walks the high-intent setup: discover the Windows host IP from Linux, aim shell tools at the real mixed or HTTP listener your core exposes, export the right http_proxy / HTTPS_PROXY / ALL_PROXY pairings, teach Git and npm explicitly, and close the loop on DNS so fake-ip profiles do not masquerade as “npm is broken.” It complements our Windows 11 first-install walkthrough and the developer-focused Cursor plus GitHub split-routing article—but the protagonist here is the WSL2 ↔ Windows boundary and the command-line tools that refuse to guess your intentions.
Why WSL2 “ignores” the proxy you already enabled on Windows
When a Windows GUI client enables system proxy, it typically asks the OS to funnel WinHTTP/WinINet-aware programs through a loopback listener such as 127.0.0.1:7897. Browsers and many desktop apps cooperate. WSL2 distributions, however, run inside a virtualized Linux kernel with separate interfaces, routing tables, and resolver configuration. From inside Ubuntu on WSL, 127.0.0.1 means the Linux instance itself, not your Windows host. Unless you explicitly bridge that gap, Git and npm will happily try to reach GitHub or the npm registry directly through the WSL default route—exactly the path your Clash rules on Windows never see.
Another subtle trap: even when you know the theory, you may paste proxy settings that worked on a bare-metal Linux laptop. On WSL2 the upstream gateway is often the virtualized switch that Microsoft attaches to your machine, and the stable way to reach Windows services is the host-facing IP, not nostalgia for localhost folklore. Newer WSL builds add optional behaviors that forward localhost from Linux to Windows, but availability and defaults change with Windows releases. For a tutorial meant to survive forum copy-paste, we anchor on the explicit host IP method first, then mention localhost forwarding as an acceleration lane once your baseline works.
What you need on Windows before touching WSL
Confirm the Windows side is boringly healthy. Open your Clash derivative—Clash Verge Rev is the common choice in 2026—and verify the core is running, a node or auto group is selected, and a browser on Windows loads the sites you care about. Note the actual listener port: many templates still mention 7890 from legacy Clash for Windows muscle memory, while modern bundles often expose a mixed port such as 7897 that serves HTTP and SOCKS on one TCP entry. Write that number down; mismatched ports are the cheapest way to waste an evening.
If you have never walked the Windows-first path, start with the Windows 11 first-setup article so you are not debugging WSL while the host profile is half-applied. For vocabulary around YAML rules after traffic reaches the proxy, the site-wide Clash tutorial still maps fuzzy concepts to inspectable objects.
Step 1 — Discover the Windows host IP from inside WSL2
You need an IP address that Linux can route to where Windows listens. Two portable recipes work across most WSL2 builds; they usually agree, and when they disagree you should trust the one that matches ip route’s default gateway.
Method A — default gateway from the routing table:
ip route show | grep -i default | awk '{ print $3}'
Method B — resolver nameserver (WSL often injects the host here):
grep -m1 '^nameserver' /etc/resolv.conf | awk '{ print $2 }'
Assign it to a shell variable so the rest of this guide stays copy-paste friendly:
export HOST_IP=$(ip route show | grep -i default | awk '{ print $3}')
echo "$HOST_IP"
Sanity-check from WSL that TCP reaches your Clash listener (replace the port with yours):
curl -v --proxy "http://${HOST_IP}:7897" https://www.cloudflare.com/cdn-cgi/trace
If curl hangs or refuses the connection, stop. Either the port is wrong, Windows Firewall is blocking cross-hypervisor access, or the core is bound to loopback only. On Windows, ensure your profile allows LAN or external binding for the listener you expect—GUI clients surface this as Allow LAN / bind address options. If you recently tightened security, revisit the LAN proxy and firewall guide; the same inbound mindset applies when WSL, not a phone, is the client.
127.0.0.1 from WSL when localhostForwarding is enabled in .wslconfig. If your build supports it and you confirm behavior with curl, you may simplify URLs to 127.0.0.1:PORT. Treat it as an optimization: master the explicit HOST_IP recipe first so you are not stuck when an update changes defaults.
Step 2 — Export proxy environment variables for interactive shells
Most CLI tools in Linux honor http_proxy and https_proxy when spelled in lowercase, and many also read uppercase variants. Conservative shells set both. For repositories and package managers that speak HTTPS over HTTP CONNECT, start with HTTP proxies pointing at your mixed port:
export HOST_IP=$(ip route show | grep -i default | awk '{ print $3}')
export http_proxy="http://${HOST_IP}:7897"
export https_proxy="http://${HOST_IP}:7897"
export HTTP_PROXY="$http_proxy"
export HTTPS_PROXY="$https_proxy"
If you prefer SOCKS5 because a template locks HTTP plain ports, align with what your core actually publishes:
export ALL_PROXY="socks5://${HOST_IP}:7897"
export all_proxy="$ALL_PROXY"
Always define no_proxy for local endpoints so internal tooling does not round-trip through a foreign node:
export no_proxy="localhost,127.0.0.1,::1"
export NO_PROXY="$no_proxy"
Persist the block in ~/.bashrc or ~/.zshrc, but guard it with a quick comment so future you remembers the Windows port may change when profiles regenerate. If you use direnv or devcontainers, mirror the same variables inside those scopes.
Teams that spawn non-interactive jobs—cron inside WSL, CI-like scripts, or Makefile targets invoked by VS Code tasks—should remember those processes might not source your shell rc files. Export variables in the wrapper script itself, or place a tiny file under /etc/profile.d/ if every user on that distro should inherit the same corporate defaults. Document the port next to the snippet; nothing is more confusing six months later than a magic 7897 constant nobody can trace.
Credential helpers deserve a mention. If you authenticate to GitHub with a helper that calls a Windows executable through interop, keep no_proxy broad enough for loopback helper traffic. When helpers fail mysteriously after proxy changes, test git credential fill in isolation before you assume Clash dropped packets.
Step 3 — Teach Git explicitly (global config + corner cases)
Git respects environment variables, yet many teams still set canonical proxies in Git config for reproducibility. Use the same scheme your curl test proved:
git config --global http.proxy "http://${HOST_IP}:7897"
git config --global https.proxy "http://${HOST_IP}:7897"
For SSH remotes, environment HTTP proxies do nothing—Git uses SSH, not CONNECT tunnels through that listener. Either switch the remote to HTTPS when you need to ride the proxy, or run SSH through a more advanced ProxyCommand that matches your security policy. Beginners conflate the two, see failures, and blame Clash; separating transports saves hours.
Corporate inspection appliances sometimes break TLS for Git; if you operate in that world, mirror your security team’s trust bundle instead of random -k flags. For open-source workflows against GitHub, focus on clean DNS and rule matches—our developer split-routing guide lists domain clusters worth pinning in YAML so Git traffic lands in the proxy group you expect.
Large monorepos sometimes ship .gitconfig snippets or per-repo overrides. If a teammate’s include.path resets proxy settings, Git will obey the last writer in the cascade. When debugging, run git config --show-origin --get-regexp proxy to see which file owns the effective value. That one command ends many Slack threads.
Binary Git LFS pulls add another choke point: they hit hosts beyond raw Git objects. If LFS objects stall while commits fetch fine, capture the failing URL from verbose logs and promote that host into your rule set instead of blaming the initial clone.
Step 4 — npm: .npmrc, registry hosts, and SSL trust
npm reads proxy settings from environment variables and from project or user .npmrc. After exporting the variables from Step 2, verify:
npm config get proxy
npm config get https-proxy
If you need hard pins, write them into ~/.npmrc (adjust host and port):
proxy=http://172.24.128.1:7897
https-proxy=http://172.24.128.1:7897
Replace the sample IP with your live HOST_IP. Mixed-content registries and alternate mirrors (company Verdaccio, Taobao legacy mirrors, pnpm fetchers) each introduce their own hostname lists; when a install fails on one registry but not another, compare which host is missing from your Clash rules, not whether npm is “broken.”
SSL errors often indicate a man-in-the-middle or a proxy that is not actually speaking TLS to the registry. After you trust the network path, strict-ssl should remain true; turning it off globally is a blunt instrument that hides real attacks.
Corepack and alternate package managers complicate the picture in 2026. pnpm and Yarn Berry read proxy environment variables but may spawn child processes that need the same inheritance. If you standardize on pnpm, verify pnpm config get http-proxy after setting globals. npx downloads can ignore project-level .npmrc depending on invocation; when a one-off CLI fails, reproduce with NPM_CONFIG_LOGLEVEL=verbose so you see which tarball host actually broke.
Private registries behind SSO often require both proxy reachability and custom CA bundles. Ship those bundles into WSL’s trust store deliberately—copying a PEM into /usr/local/share/ca-certificates/ and running update-ca-certificates is dull but reliable. Mixing Windows and Linux trust stores without documentation is how “works on my machine” turns into a ritual.
Step 5 — DNS, fake-ip, and why lookups look “stuck”
Clash profiles frequently enable fake-ip for performance: applications ask for a resolver, receive a synthetic address from a dedicated pool, and the proxy core learns the real destination from TLS SNI or protocol sniffing. That model is powerful on Windows where the core coordinates everything. Inside WSL2, if you force tools to use a Linux resolver that bypasses the core’s DNS expectations, you can observe bizarre partial failures—some npm packages resolve, others time out, GitHub pings look flaky.
Practical debugging sequence:
- From WSL, run
dig registry.npmjs.orgorgetent hosts github.comand note which server answers. - Open your Windows client log and watch DNS queries while repeating the command; decide whether traffic should be
DIRECTor forwarded. - If you recently edited YAML, confirm
dnssections and anynameserver-policyoverrides still match how Microsoft injects resolvers into WSL.
When symptoms scream “DNS split brain,” compare against the deep dives in Clash TUN mode explained—the article is TUN-centric, but the DNS mental model transfers to command-line troubleshooting.
Advanced WSL users sometimes disable automatic resolv.conf generation via /etc/wsl.conf to pin corporate resolvers. That is legitimate, but it also means you—not Microsoft—own every downstream DNS decision. If you flip generateResolvConf = false, revisit the file after each distro upgrade; silent reversion has bitten teams who templated the setting once and forgot it.
IPv6 paths add spice. If your Windows stack prefers IPv6 for certain CDNs while WSL lacks a working v6 route, tools may race Happy Eyeballs timeouts that look like “random slowness.” Disabling IPv6 globally is rarely the right answer, but knowing whether your resolver returns AAAA records helps you interpret Wireshark or Clash logs without panic.
Step 5b — Windows Firewall and “it works until I reboot”
Even when curl succeeds once, intermittent blocks after sleep or VPN reconnect often trace to Windows Defender Firewall profile drift. If you run Clash with allow-lan semantics or an explicit bind address, mirror the discipline from the LAN proxy article: create a narrowly scoped inbound rule for your mixed TCP port on the Private profile, name it like a responsible adult, and delete stale rules when you change ports.
WSL traffic originates from the virtual switch; it is still inbound to the Windows listener from Windows’ perspective. When in doubt, open Windows Defender Firewall with Advanced Security, watch counters on the rule while you hammer curl from WSL, and let empirical packet counts—not forum opinions—guide you.
Step 6 — Mixed networking modes and corporate VPN interference
WSL2 participates in what Microsoft calls a mixed networking story: your PC may have Wi-Fi, Ethernet, VPN tunnels, and Hyper-V switches simultaneously. Corporate VPN clients sometimes capture all routes from Windows but do not propagate the same policies into the WSL virtual NIC. Symptom: Windows browser works through Clash plus VPN split tunneling, while WSL traffic takes a different default gateway and dies.
When that happens, collect facts before mutating configs: print Windows ipconfig, WSL ip addr, and compare default routes. Sometimes the fix is as simple as restarting the VPN after WSL starts; sometimes you must script HOST_IP discovery because the gateway moved. Keep a short notebook of addresses when you dock and undock—laptop users feel this pain first.
Microsoft continues to evolve optional mirrored networking modes for WSL that change how localhost and multicast behave. If you participate in insider builds, read release notes when networking feels different after an update—your automation should key off observable routes, not hard-coded assumptions about yesterday’s hypervisor layout.
Also remember that Clash’s own TUN interface on Windows can interact with third-party VPN drivers. If enabling TUN on the host suddenly breaks WSL while system-proxy mode still works, you are observing kernel driver ordering, not a Linux bug. Temporarily test with system proxy only, stabilize routes, then reintroduce TUN with logging—exactly the sort of A/B workflow we champion across the site.
Step 7 — Validate end-to-end with boring commands
Good engineers log everything. After applying settings, run this checklist:
curl -I https://github.comthrough the proxy variables you exported.git ls-remote https://github.com/microsoft/vscode.git HEADto exercise HTTPS Git without cloning weight.npm view react versionto confirm registry metadata retrieval.- Optional:
pnpm pingoryarn config get registryif your monorepo standardized on alternates.
Watch your Windows client’s connection list while the commands run. You should see flows attributed to WSL’s source addresses. If Windows shows silence, the packets never reached Clash; if Windows shows REJECT or odd groups, your YAML—not WSL—is the next edit surface. When rules need refinement, the custom rules tutorial explains how to keep overrides merge-safe.
Capture evidence once, store it with your dotfiles: a redacted screenshot of the successful curl trace, the effective git config origins list, and the npm config list excerpt. Future teammates inherit WSL pain in every onboarding; gifting them a reproducible recipe pays exponential dividends.
Bonus — apt, container pulls, and other CLIs that share the same pain
This article leads with Git and npm because searchers type those verbs first, but the same HOST_IP trick feeds apt on Debian-based distros if you must route http:// mirrors through an HTTP proxy:
echo 'Acquire::http::Proxy "http://'"$HOST_IP"':7897/";' | sudo tee /etc/apt/apt.conf.d/95proxy
echo 'Acquire::https::Proxy "http://'"$HOST_IP"':7897/";' | sudo tee -a /etc/apt/apt.conf.d/95proxy
Many modern mirrors prefer HTTPS; support varies by mirror geography. Test with a small apt update before you bake this into golden images.
Container workflows echo the lesson: Docker Desktop’s Linux VM is not WSL2, but engineers often juggle both on the same laptop. If you later run rootless podman or docker inside WSL, you may need to propagate proxy settings into systemd user units or daemon JSON—another reason centralized environment documentation beats tribal knowledge.
Language-specific tooling—Go modules, Rust crates, Python pip—generally honors HTTPS_PROXY once the environment is clean. When a particular toolchain ignores env vars, read its manual page: some require uppercase-only variables, some need tool-specific config files, and almost none read the Windows registry on your behalf.
Common failure modes (and the first knob to turn)
“Connection refused” to the proxy port
Wrong port, core not running, or listener bound to Windows loopback only. Re-check mixed-port in the active profile and GUI toggles for LAN binding.
Works in Windows Terminal PowerShell, fails in WSL
You still have not exported Linux variables or you aimed at 127.0.0.1 from WSL without localhost forwarding. Re-run the HOST_IP recipe.
Git works, npm hangs on tarballs
Often DNS or a blocked CDN domain. Inspect resolver output and add DOMAIN rules for the failing host your log highlights.
Everything flakes when VPN connects
Route precedence changed. Recompute HOST_IP after VPN establishment or insert a script hook.
Security housekeeping
Opening listeners beyond loopback increases exposure. If you enable allow-lan for WSL convenience, pair it with firewall rules scoped to private profiles, and disable it on café Wi-Fi. WSL is not a magic security boundary; treat cross-environment proxying like any other network service you operate.
Closing: make WSL2 explicit, then automate
WSL2 will not read your mind, and it certainly will not inherit Windows proxy GUI state by default. The fix is disciplined: compute the Windows host IP, point http_proxy, https_proxy, Git, and npm at the real Clash listener, align DNS with your fake-ip strategy, and verify with logs—not superstition. Compared with toggling mystery checkboxes, this sequence turns “Git and npm ignore the proxy” into a reproducible dev environment you can script once and reuse across machines.
When you want a maintained desktop client to anchor the workflow, use this site’s download page as the primary install path—Download Clash for free and experience the difference.