Fix GitHub and npm Timeouts With Clash: HTTP_PROXY and Git Proxy on Windows, macOS, and Linux
Your browser loads GitHub, yet git clone stalls, npm install crawls, and curl prints TLS timeouts. That split is normal: many GUI apps follow the system proxy that Clash toggles, while shells and developer tools often ignore it unless you export HTTP_PROXY, HTTPS_PROXY, and friends. This guide shows how to aim terminals at Clash’s HTTP listener (usually the mixed port), add a dedicated Git proxy, tune npm and other CLIs, and verify everything without guesswork.
Why Clash can be “on” while the terminal still fails
Clash clients typically flip the operating system’s proxy settings so Safari, Chrome, or Edge pick up a PAC file or a fixed HTTP proxy. That path is excellent for browsing. Command-line programs are a different contract: POSIX shells do not magically inherit a “system proxy” object the way a browser does. Instead, most network stacks look for conventional environment variables—HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and NO_PROXY—or their lowercase spellings that some libraries still honor.
When those variables are empty, git may try to reach github.com directly. If your uplink to that ASN is flaky, you observe slow TLS handshakes, reset connections, or endless retries during git clone over HTTPS. npm behaves similarly: registry metadata and tarball downloads are plain HTTPS; without a proxy, the client keeps backing off until you assume “npm is broken” when the real issue is routing. curl and wget are honest reporters—if you pass -v, you will see exactly where the handshake stops.
Another subtle gap appears when only SOCKS5 is exposed. Some stacks want an http:// upstream for CONNECT tunneling, while others prefer socks5h:// so DNS happens on the proxy side. Clash’s mixed port exists to reduce that friction: one listener speaks HTTP and SOCKS on the same numeric port, which keeps tutorials short and configs portable across Windows, macOS, and Linux.
Quick mental model
Treat Clash as two products: the tunnel your rules feed, and the local front door (mixed port) CLIs must be told to use. Rules decide where packets exit; env vars decide whether a CLI knocks on that front door at all.
Find the HTTP/SOCKS listener in your Clash client
Open your profile YAML or the client’s settings screen and locate mixed-port. Community defaults often use 7890, but your pack may choose another value to avoid collisions. If mixed-port is disabled, separate port (HTTP) and socks-port lines still work—point HTTP_PROXY at the HTTP port and ALL_PROXY at SOCKS if you split them.
Confirm the listener is bound where you expect. Loopback-only (127.0.0.1) is safest for laptops. If you enabled allow-lan for phone testing, you can still use 127.0.0.1 on the same machine; just resist the temptation to hardcode a LAN IP unless you understand the threat model. After edits, restart the core or apply the profile so the port is actually open—try curl -I http://127.0.0.1:7890 from a shell; you should not get “connection refused”.
Sanity checks before exporting variables
- Clash shows connected and your browser can load https://github.com.
mixed-port(or HTTP port) matches the number you will embed in proxy URLs.- Rule mode is not forcing unexpected DIRECT paths for the domains you are testing.
- No second VPN or filter driver is intercepting loopback in a way that blocks CONNECT.
HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and NO_PROXY
Most developers set the pair:
HTTP_PROXY=http://127.0.0.1:7890HTTPS_PROXY=http://127.0.0.1:7890
HTTPS traffic still uses an HTTP proxy: the client issues a CONNECT tunnel, then runs TLS through it. That confuses newcomers, but it is why both variables can share the same http:// URL.
ALL_PROXY catches protocols outside classic HTTP—think git+ssh only when tools fall back to SOCKS-aware stacks, or languages that consult ALL_PROXY for generic outbound sockets. A typical Clash setup uses either:
ALL_PROXY=socks5h://127.0.0.1:7890when you want DNS on-proxy, or- the same
http://127.0.0.1:7890if you prefer one scheme everywhere.
NO_PROXY is your escape hatch. Add comma-separated hostnames and CIDRs that must bypass the proxy: private networks (localhost, 127.0.0.1, ::1), corporate artifact registries on RFC1918 space, or a Kubernetes API IP. Forgetting NO_PROXY is how teams accidentally hairpin internal CI traffic through a commercial exit.
Security note
Proxy env vars apply to every child process in that shell. Close the terminal when you are on untrusted Wi‑Fi if you pasted credentials into rc files, and never share screen recordings with active proxy URLs if your LAN exposes allow-lan.
Windows: PowerShell, CMD, and persistent profiles
For a one-off session in PowerShell:
$env:HTTP_PROXY="http://127.0.0.1:7890"
$env:HTTPS_PROXY="http://127.0.0.1:7890"
$env:ALL_PROXY="socks5h://127.0.0.1:7890"
$env:NO_PROXY="localhost,127.0.0.1,::1"
git ls-remote https://github.com/git/git.git HEAD
CMD uses set VAR=value in the same spirit. Git Bash and MSYS tools read the environment too—just restart the shell after you change machine-wide settings.
To persist for your user, Windows offers setx or the System Properties UI. Prefer per-user scope unless you maintain golden images. Remember setx truncates long values and does not touch the current shell, so open a new terminal after running it. Many teams skip persistence and instead drop a tiny proxy-on.ps1 / proxy-off.ps1 pair in a dotfiles repo—less surprise than forgetting a global variable during a production incident.
If you live inside Windows Terminal profiles, you can prepend the export lines to a dedicated profile that launches Clash-aware shells, leaving default profiles clean. That pattern mirrors how macOS users maintain separate tabs.
macOS and Linux shells: bash, zsh, and fish
Add the exports to ~/.bashrc, ~/.zshrc, or fish’s config.fish. Example for bash/zsh:
export HTTP_PROXY="http://127.0.0.1:7890"
export HTTPS_PROXY="http://127.0.0.1:7890"
export ALL_PROXY="socks5h://127.0.0.1:7890"
export NO_PROXY="localhost,127.0.0.1,::1"
Reload with source ~/.zshrc or open a new tab. On macOS, GUI apps still follow system proxy, but Terminal.app and iTerm2 only see what the shell exports—there is no contradiction, just two parallel mechanisms.
Linux desktops vary: some terminal emulators inherit a session bus environment if you export variables in ~/.profile (login shells) versus ~/.bashrc (interactive non-login). If your distro launches terminals as non-login shells, keep exports in the rc file you actually source. Server installs without a GUI should set the same variables in the service unit or CI job YAML so unattended git fetch runs succeed.
Lowercase duplicates
Some runtimes read http_proxy / https_proxy. Duplicating both cases is harmless and saves a frustrating hour when a Python wheel or Ruby gem only checks lowercase.
Git: http.proxy, https.proxy, and protocol quirks
Git respects environment variables, yet explicit configuration removes ambiguity—especially when a GUI client and a terminal share the same repo.
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890
For SSH remotes ([email protected]), HTTP proxy variables do nothing. You either switch the remote to HTTPS, or tunnel SSH via ProxyCommand with something like nc through a SOCKS listener—outside the narrow scope of this page, but worth remembering when someone says “Git proxy failed” while still on SSH URLs.
When handshakes succeed but clones crawl, check HTTP version negotiation. Some networks mishandle HTTP/2 to certain exits; forcing HTTP/1.1 is a known workaround:
git config --global http.version HTTP/1.1
Undo mistakes with git config --global --unset http.proxy (and the https counterpart) once you are done traveling.
npm, Yarn, and pnpm
Package managers usually honor proxy env vars, but explicit keys help when a project ships an .npmrc that overrides behavior. Common entries:
proxy=http://127.0.0.1:7890/
https-proxy=http://127.0.0.1:7890/
Keep the trailing slash consistent with your registry documentation. For enterprise registries that use private TLS, you may need strict-ssl=false temporarily—treat that as a smell, fix the trust store, then re-enable verification.
Yarn v1 mirrors the same keys; Yarn Berry reads HTTP_PROXY but also supports yarn config set for teams that check config into docs. pnpm follows npm closely. Regardless of tool, watch for registry= pointing at an internal mirror: you still need outbound HTTPS to that mirror, so align NO_PROXY if it lives on a private IP.
If installs succeed but postinstall scripts download blobs from GitHub Releases, remember those requests are separate HTTP clients—either they inherit env vars, or they hardcode fetch options. When debugging, run the install with npm_config_loglevel=verbose and read which URL stalled.
curl, wget, apt, and other CLIs
Test routing with:
curl -I https://github.com
curl -x http://127.0.0.1:7890 -I https://github.com
If the second command works while the first fails, your shell env is still empty. wget honors https_proxy similarly.
Debian and Ubuntu apt does not read standard env vars for repository fetching; it wants Acquire::http::Proxy in a file under /etc/apt/apt.conf.d/. Fedora dnf uses proxy= in /etc/dnf/dnf.conf. Container builds copy those quirks—Dockerfile ENV HTTP_PROXY helps application layers, but the distro package manager may still need its own stanza.
WSL2, Docker, and remote machines
WSL2 has a separate TCP stack from Windows. Loopback forwarding improved over time, yet many users still need the Windows host IP or mirrored networking tweaks. Rather than duplicating that thread here, follow the dedicated WSL2 and Clash guide for .wslconfig, host addresses, and optional portproxy—then return here for the same HTTP_PROXY exports inside Linux.
Docker Desktop can inherit proxy settings, but daemon pulls and buildkit may need separate JSON keys. When in doubt, set build-args for HTTP_PROXY during docker build so every layer sees the same egress.
On a headless VPS where Clash runs as a systemd service, pair this article with the Linux Meta and systemd tutorial so the core stays up; exports belong in the shell or unit that launches your maintenance scripts.
Troubleshooting TLS stalls and “works in browser only”
Symptoms cluster into a few buckets:
- Connection refused: wrong port, Clash not listening, or a firewall rule blocking loopback (rare but real on hardened images).
- Handshake timeout: packets leave the machine but the exit cannot complete TLS—try another node, verify UDP is not required for your path, and inspect Clash logs for rule hits.
- Certificate errors: a corporate MITM appliance expects its root installed; without it, curl and git fail fast while a browser might trust the enterprise store.
- HTTP/2 quirks: downgrade Git or curl to HTTP/1.1 temporarily to isolate middlebox interference.
Always compare a failing CLI with curl -v through explicit -x. If explicit proxy works but env vars do not, you are in the wrong shell session or a tool clears the environment (sandboxed IDEs sometimes strip variables unless whitelisted).
DNS deserves a mention: socks5h pushes hostname resolution to the proxy side, which helps when local DNS lies but the remote resolver is clean. If you recently toggled fake-ip or sniffer options, revisit the DNS and fake-ip article—browser fixes do not guarantee CLI resolution parity.
FAQ
Should ALL_PROXY use SOCKS or HTTP?
Either can work with Clash’s mixed port. SOCKS with socks5h is slightly more explicit about DNS. Pick one style per team and document it—mixing schemes without comments creates ghost incidents.
How do I apply this in CI without leaking secrets?
Store proxy URLs in the CI secret manager, inject them as masked variables, and scope them to jobs that truly need external registry access. Pair with NO_PROXY for internal artifact hosts.
How do I disable everything quickly?
Unset variables in the shell (unset HTTP_PROXY HTTPS_PROXY ALL_PROXY), remove Git config keys, and comment npm entries. Closing the terminal is not enough if you used setx on Windows—delete the user variables or overwrite them with empty strings deliberately.
Checklist before you blame GitHub
- Browser test passes while
curl https://github.comfails → configure env vars. - Explicit
curl -xworks → exports or tool-specific proxy missing. - Git HTTPS still fails → add
http.proxy/https.proxy, check SSH vs HTTPS remotes. - npm metadata loads but tarballs 404 → separate URL or mirror issue, not initial proxy discovery.
- WSL2 involved → read the WSL2 guide for host IP and mirrored networking first.
Keep Clash predictable for developers
Once terminals and browsers share the same local front door, developer workflows stop oscillating between “VPN on” and random --registry hacks. Clash rules still decide which upstream leaves the country; env vars simply make sure your tools knock on the door you already trust.
Make terminals use the same proxy as your browser
Export HTTP_PROXY, wire Git and npm, and verify with curl—GitHub and registry timeouts fade once CLIs hit Clash’s mixed port.
Download Clash