1. System proxy, environment variables, and TUN: what npm actually uses
Node’s built-in HTTP clients and the stacks beneath npm and pnpm generally honor the de-facto standard proxy environment variables. Whether a given process also picks up the Windows “system proxy” depends on how it was spawned, which runtime version you run, and whether a parent tool stripped the environment. That ambiguity is why teams who care about reproducible installs usually standardize on explicit shell exports for HTTP_PROXY and HTTPS_PROXY, then document the same variables for CI runners. On the Clash side, Windows users typically encounter three egress shapes: a GUI-driven system proxy that points WinHTTP at 127.0.0.1:mixed-port, a listener that is up but unused by the terminal, or TUN mode that captures traffic at the OS layer. Each can work; the failure pattern we are solving here is “half the traffic followed one path and half another,” which produces flaky ETIMEDOUT errors that disappear when you simplify to one intentional path for the package manager.
If you already enabled system proxy in Verge Rev or a similar shell, still verify what your current terminal sees. In PowerShell, echo $env:HTTP_PROXY should print the value you expect; in CMD, echo %HTTP_PROXY%. Git Bash maintains its own profile scripts and may not mirror PowerShell defaults. The mental model is the same one we use when explaining container HTTP_PROXY: environment variables answer whether the application voluntarily forwards through an HTTP CONNECT port, while TUN answers whether the kernel steers packets before user space gets a choice. You can combine both, but debugging is faster when you can flip the terminal back to “env vars only” and watch Clash logs for a single obvious hop.
If you also develop inside WSL2, remember that 127.0.0.1 inside Linux is not your Windows host. Follow the dedicated WSL2 networking article for mirrored-network and host-IP patterns. The steps below target Windows shells running npm and pnpm on the host OS itself.
2. Clash prerequisites on Windows: listener, bind address, system proxy toggle
Treat your mixed-port (or dedicated HTTP port) like any other TCP service: when HTTP_PROXY=http://127.0.0.1:7890 is set, something must be listening on that address and port. For local-only development, binding to 127.0.0.1 is correct and avoids exposing the inbound to the LAN. If you intentionally share the same listener with other devices, you will need allow-lan and matching Windows Defender Firewall rules—see allow-lan on Windows for the full checklist.
Desktop bundles often separate “system proxy” and “TUN” toggles. It is easy to enable only one and assume the other followed. If you want a visual tour of how those pieces fit on modern Windows, skim Clash Verge Rev on Windows 11 first, then return here to wire the terminal with minimal moving parts. If subscriptions or nodes are still unhealthy, fix that before chasing registry issues—walk through subscription import and connectivity, confirm browser tests, then run npm ping or pnpm config get registry so you do not mis-attribute upstream outages to NO_PROXY.
Logging discipline matters. When a tarball host times out, copy the exact hostname from the Clash connection log (or from npm verbose output) before you widen DOMAIN-SUFFIX rows. Over-broad CDN rules are how people accidentally send half of the public internet through the wrong policy group.
3. Persisting HTTP_PROXY, HTTPS_PROXY, and NO_PROXY on Windows
For a single PowerShell session, set variables before you run pnpm install or npm ci. Replace 7890 with your actual mixed-port:
# PowerShell session: send Node tooling through local Clash HTTP inbound $env:HTTP_PROXY = "http://127.0.0.1:7890" $env:HTTPS_PROXY = "http://127.0.0.1:7890" $env:ALL_PROXY = "http://127.0.0.1:7890" $env:NO_PROXY = "localhost,127.0.0.1,::1,.local,npmmirror.com,registry.npmmirror.com"
Expand NO_PROXY to match your reality: corporate suffixes such as .corp.example.com, on-prem Git hosts, internal artifact domains, and every domestic mirror hostname your team uses. Some runtimes read lowercase no_proxy; Node generally follows uppercase first, but duplicating both in team documentation removes pointless variance between macOS/Linux colleagues and Windows developers. Use comma-separated entries; avoid stuffing secrets into proxy URIs that might leak through screen shares or committed dotfiles.
To persist values for your user account, use the Environment Variables dialog (System Properties → Advanced → Environment Variables) or setx from an elevated prompt. Remember setx truncates long strings and requires opening a new terminal to take effect—many “it did not work” reports are simply stale shells. A popular developer workflow is to place the four assignments in a PowerShell profile so every “dev” terminal inherits the same contract while IDEs launched from Explorer remain untouched.
A subtle but frequent mistake is writing HTTPS_PROXY=https://127.0.0.1:7890 when your local inbound only speaks HTTP CONNECT. The correct form is still http://127.0.0.1:7890: the letter S in HTTPS_PROXY names the target scheme your application wants to reach, not the scheme of the proxy hop itself. Getting this wrong produces handshake failures before any meaningful bytes move.
4. npm and pnpm: registry, lockfiles, cache, and proxy inheritance
The registry URL controls where metadata and default tarball URLs originate. Pointing registry at a domestic mirror speeds up JSON metadata, but individual packages—especially older ones—may still reference tarballs on registry.npmjs.org, npm.pkg.github.com, or a vendor-specific CDN. That split is why “metadata flies, download stalls” is such a recognizable symptom. Start with npm config get registry and pnpm config get registry, then run npm ping to confirm round-trip health against the active registry host.
Private registries at https://npm.internal.example/ must appear in NO_PROXY alongside the bare hostname and any suffix forms your tooling emits. If you skip this, pnpm will cheerfully tunnel internal HTTPS through Clash, and your exit node will try to complete TLS on behalf of an address it cannot legitimately reach—yielding confusing certificate errors that look like malware interception. After changing registry settings, consider clearing local caches when debugging corruption: npm cache verify and pnpm store prune are blunt but effective instruments when you suspect half-written tarballs from aborted runs.
Lockfiles anchor resolved URLs. A repository created on a machine with one registry mirror may embed tarball URLs that surprise teammates on another continent. Treat lockfile drift as a collaboration issue first, then a proxy issue second. Both npm and pnpm ultimately rely on Node’s HTTP stack for downloads, so once the environment variables are coherent, most “works on my laptop” differences reduce to registry URLs, DNS, or corporate SSL inspection—not mystical package-manager bugs.
When you combine editor traffic with CLI installs, keep domain lists mentally separated: our Cursor routing article focuses on IDE and model APIs, while this page focuses on package distribution hosts. The same Clash profile serves both, but your expectations for NO_PROXY differ—editors may need aggressive proxying while internal registries need aggressive bypassing.
5. Split rules: domestic mirror DIRECT, npmjs and GitHub via proxy
Environment variables decide whether traffic is offered to the local inbound. Split rules decide what happens after that—whether mihomo sends the flow DIRECT to the ISP or forwards it to a remote policy group. Your business intent should read consistently in both layers: domestic mirror hostnames should bypass the tunnel via NO_PROXY and appear early in the ruleset as DIRECT rows; hosts that genuinely require an overseas exit should map to a stable outbound group even when NO_PROXY does not apply. Replace PROXY below with your real policy group name.
# Example mihomo rules — adjust group names and domains to your subscription
rules:
- DOMAIN-SUFFIX,npmmirror.com,DIRECT
- DOMAIN-SUFFIX,registry.npmmirror.com,DIRECT
- DOMAIN-SUFFIX,registry.npmjs.org,PROXY
- DOMAIN-SUFFIX,npm.pkg.github.com,PROXY
- DOMAIN-SUFFIX,github.com,PROXY
- DOMAIN-SUFFIX,objects.githubusercontent.com,PROXY
Real dependency trees pull binaries from *.amazonaws.com, *.cloudfront.net, releases.download.github.com, and language-specific CDNs. Blindly classifying entire public suffixes as DIRECT or PROXY tends to break unrelated traffic. Prefer incremental updates: reproduce the failure with npm install --verbose, copy the failing hostname, add a precise DOMAIN-SUFFIX or DOMAIN row ahead of your catch-all GEOIP or MATCH, then retest. For latency-sensitive exits, pair this habit with the guidance in url-test and failover policy groups so automated node selection does not fight your manual domain rows.
When HTTP_PROXY already points at Clash, a YAML rule of DIRECT still means “leave through the default WAN path from the proxy machine’s perspective,” not “skip the proxy variable.” If you need certain hostnames to ignore the proxy entirely, that belongs in NO_PROXY. Keeping the two concepts distinct prevents endless toggling between “I fixed YAML” and “the tool still used CONNECT.”
| Goal | Primary knob | Notes |
|---|---|---|
| Domestic mirror metadata | NO_PROXY plus explicit DIRECT rules |
Speeds local resolution; tarballs may still need separate rows. |
| Official npmjs tarballs | Rule rows toward your PROXY group | Align with hostnames seen in logs, not assumptions from README titles. |
| On-prem registry | NO_PROXY (mandatory) |
Prevents internal TLS from crossing an unrelated exit node. |
6. Optional .npmrc knobs: proxy keys, strict-ssl, and scoped registries
Beyond environment variables, npm respects proxy, https-proxy, and noproxy keys inside per-user or per-project .npmrc. These can be helpful when a GUI-launched IDE spawns Node without inheriting your PowerShell profile, but they also duplicate truth—drift between shell exports and committed .npmrc files is a frequent source of “CI passes locally but fails on the build server.” Pick one layer as canonical for your team and treat the other as an override only when tooling demands it.
Scoped registries (@your-scope:registry=...) interact with proxy settings per scope. When only one scope should bypass the tunnel, mirror that intent both in NO_PROXY hostnames and in Clash rules so DNS and routing stay aligned. Avoid flipping strict-ssl=false as a permanent workaround for proxy-related TLS errors; it hides real MITM or inspection problems and makes future audits painful.
pnpm honors the same proxy environment variables for fetch operations while adding its own content-addressed store semantics. Large monorepos amplify any mis-proxying because a single bad hop can stall dozens of concurrent downloads. If you use only-built-dependencies or custom pnpmfile.cjs hooks, remember those steps still inherit the process environment—there is no separate “pnpm network stack” to configure.
7. Mixed-configuration failures and how logs narrow the blast radius
Symptom class A: metadata resolves instantly from a domestic mirror, yet the progress bar freezes on one tarball. Grab the hostname from verbose logs, confirm how Clash classified it, and either adjust the outbound health, add a mirror rewrite at the registry, or add a narrowly scoped rule. Symptom class B: corporate hosts fail with proxy authentication or foreign certificates—almost always a missing NO_PROXY entry or an SSL inspection appliance that needs its root trusted deliberately, not masked by disabling TLS verification.
Symptom class C: “everything broke when I opened the VPN” usually means multiple interceptors stacked—system proxy plus TUN plus browser-only extensions—each trying to own DNS. Temporarily disable extras, keep only the local Clash inbound plus explicit env vars, and rebuild understanding from a clean baseline. Symptom class D: different terminals disagree because Git Bash reads ~/.bashrc while PowerShell reads a profile under Documents; unify onboarding docs so new hires stop cargo-culting mixed snippets from Stack Overflow threads dated five years ago.
When in doubt, compare curl -v https://registry.npmjs.org with and without proxy variables in the same shell. If curl succeeds while pnpm fails, you are looking at tool-specific configuration (custom cafile, different env inheritance), not a dead node. If both fail, climb back down the stack to DNS, firewall, and listener bind addresses before touching subscription URLs.
8. Printable verification checklist
Wiring npm and pnpm on Windows through a host-resident Clash core is less about secret flags and more about aligning two layers of intent: explicit HTTP_PROXY / HTTPS_PROXY / NO_PROXY contracts for the Node process, and deliberate split rules that send registry.npmjs.org and GitHub-adjacent hosts through a policy group that you actually trust for large downloads. Compared with constantly toggling the global system proxy or rewriting hosts files, the combination scales better across teammates and CI images, and it fails in ways that logs can explain. When you want a maintained installer channel and update cadence that matches the rest of the site, start from our download center, then layer terminal exports and YAML refinements on top. Compared with ad-hoc scripts, a single well-instrumented Clash profile tends to feel calmer when you alternate between browsing documentation and pulling hundreds of small packages—both activities benefit from the same transparent routing discipline.
Related Reading · topic cluster
Hand-picked deep-dives on the same topic — practical Clash routing guides in the same category.
Clash on but Browsers Still Direct? Turn Off Secure DNS on Windows 2026
Clash shows connected on Windows but Chrome or Edge still behave like a direct line? Turn off browser and OS secure DNS and DoH, then re-align the Windows syste…
Read moreHow to Route WSL2 Traffic Through Windows Clash: Mirror Networking and Localhost Setup (2026)
WSL2 apt, git, curl, or npm not using the Clash you run on Windows? 127.0.0.1 is the WSL loopback—not the host. Find the Windows IP from the default route or re…
Read moreHow to Fix Clash Subscription Update Errors on Windows: TLS, DNS, and Log Steps
Subscription link opens in the browser but Clash keeps timing out or showing TLS or certificate noise? On Windows, read mihomo logs to split DNS resolution, TLS…
Read more