Developer Lab · · Approx. 18 min read

Fix pip Install Timeouts: Route PyPI and files.pythonhosted.org in Clash (2026)

When pip install spins forever, the failure is rarely “Python is broken.” Modern Python package management splits work across two familiar surfaces: JSON-ish index metadata from PyPI (or a private index), and bulky artifacts—wheel binaries or sdist source drops—usually served from files.pythonhosted.org or hostnames it redirects to. On constrained or flaky networks, half of that pipeline might ride your proxy while the other half attempts a timeout on a direct path, which produces the classic “metadata flew, download died” story. This article gives copy-paste Clash routing patterns (compatible with mihomo cores), pairs them with sane DNS expectations, and shows how to cross-check connection logs so you stop guessing. It complements our Windows npm and pnpm routing guide, but targets the pip resolver and PyPA CDNs explicitly.

1. Why pip hits PyPI and files.pythonhosted.org differently

Think of pip as a picky downloader with two jobs. First it asks an index—by default https://pypi.org/simple/—for the name of a release, compatible tags, and a URL where the wheel or sdist lives. That response is small, cacheable, and often quick even on mediocre links. Second, it opens a fresh connection to the artifact host—very frequently files.pythonhosted.org—and streams megabytes. If your Clash policy sends the index host through a stable exit but leaves the artifact host on a path that your ISP throttles, or the opposite, you will see intermittent failures that feel like “random” CI breakage. Corporate proxies and split-horizon DNS amplify the effect because the resolver answer can differ between lunchtime and VPN state.

Private indices deserve the same mental model. An internal DevPI might serve metadata on pypi.corp.example while still redirecting public wheels back to PyPI infrastructure. If your YAML only whitelists the internal hostname, public artifact pulls remain vulnerable. Likewise, teams that mirror tarballs to MinIO or Artifactory still sometimes forget to rewrite every redirect, which suddenly reintroduces files.pythonhosted.org mid-install. Routing policies should follow observed hostnames, not assumptions baked into requirements files.

The npm ecosystem suffers a parallel split between registry metadata and tarball CDNs; our Node-focused guide explains NO_PROXY layering that translates directly: metadata and blobs are not guaranteed to share a route, so duplicate the discipline here with Python package management hosts.

2. Clash baseline: listeners, system proxy, TUN, and mixed traffic

Before touching pip, prove the mihomo or Clash core is alive the way you intend. Most desktop bundles expose a mixed-port that accepts HTTP CONNECT for TLS targets. Binding to 127.0.0.1 is correct for solo development; widening to the LAN demands allow-lan and firewall awareness as outlined in our LAN sharing checklist—still relevant when a colleague’s laptop borrows your mixed port.

Three common shapes appear in practice. System proxy pushes compliant apps through 127.0.0.1:mixed-port without terminal tweaks. Explicit env vars target shells and CI agents. TUN captures packets early, which helps stubborn binaries but can stack confusingly with the other two. If installs succeed only when TUN toggles on, you probably never exported HTTP_PROXY for the terminal and were relying on kernel capture instead—a fragile habit on headless boxes.

When subscriptions misbehave, fix nodes before rewriting YAML for PyPA. Start with subscription import basics and latency tooling from url-test and fallback groups; a dead exit masquerades as a timeout even when rules are perfect.

3. Environment variables pip actually respects

PEP 668 aside, network-wise pip follows the same ecosystem defaults as many CLI tools: uppercase HTTP_PROXY and HTTPS_PROXY pointing at an HTTP proxy URL (even for HTTPS downloads—remember the scheme of the proxy hop stays http://), plus optional lowercase duplicates for picky libraries. Add NO_PROXY entries for localhost, link-local addresses, corporate domains, and any on-prem index that must never traverse a foreign exit. Missing exemptions is how you earn mysterious certificate errors that tempt people to disable TLS verification—do not.

# Example POSIX shell — swap port for your mixed-port
export HTTP_PROXY="http://127.0.0.1:7890"
export HTTPS_PROXY="http://127.0.0.1:7890"
export NO_PROXY="localhost,127.0.0.1,::1,.corp.example,pypi.corp.example"
python -m pip install --upgrade pip

On Windows PowerShell, mirror the assignments with $env:HTTP_PROXY=... as shown in the npm article; Git Bash and PowerShell do not automatically share profiles. For containers routing through a host-resident core, reuse the mental model from Docker host proxy gateway guidance—replace Node examples with python -m pip invocations.

Conflicting layers cause the worst heisenbugs: TUN captures packets while HTTP_PROXY also wraps the same flow, or system proxy disagrees with shell exports. When triaging, pick one deliberate path—often “env vars only into local mixed port”—disable extras briefly, and re-run python -m pip install -v small-wheel-package so verbose logs print the exact URL being fetched.

4. Mihomo rules: pinning pypi.org, files.pythonhosted.org, and mirrors

Environment variables decide whether pip offers traffic to Clash; YAML split rules decide which outbound group handles it afterward. Place narrowly scoped DOMAIN or DOMAIN-SUFFIX rows above sweeping matchers such as GEOIP or final MATCH. Swap PROXY for your real policy group name (for example 🚀 Auto or 节点选择 depending on your profile).

# Example rules fragment — adjust group label and mirrors to taste
rules:
  - DOMAIN-SUFFIX,pypi.org,PROXY
  - DOMAIN-SUFFIX,pythonhosted.org,PROXY
  - DOMAIN-SUFFIX,files.pythonhosted.org,PROXY
  - DOMAIN-SUFFIX,pypi.io,PROXY
  - DOMAIN-SUFFIX,pypi.python.org,PROXY
  - DOMAIN-SUFFIX,python.org,PROXY
  - DOMAIN-SUFFIX,test.pypi.org,PROXY

If you rely on regional mirrors, duplicate the intent: domestic mirrors you want DIRECT should appear early with consistent NO_PROXY coverage so the client and core agree. Blindly dumping entire public suffixes like amazonaws.com into DIRECT or PROXY breaks unrelated services; add hosts only after you see them in pip install -v or mihomo connection logs.

Symptom in plain English Likely routing gap What to confirm first
Version list loads; wheel download bar stuck Artifact host not matching proxy policy Log line for files.pythonhosted.org or redirect target
TLS handshake retry loops Split DNS answers versus fake-ip expectations DNS tab in dashboard; compare domain mode guidance
Private index OK; public wheels fail Corporate split tunnel missing PyPI CDNs Verbose pip URL after “Downloading …”

5. DNS fake-ip versus redir-host: symptoms that look like pip hangs

Clash Meta introduced nuanced DNS modes that confuse newcomers. fake-ip returns synthetic addresses quickly so rules can steer flows by domain, but anything that bypasses the core’s resolver—hard-coded IP usage, mis-ordered rules, or apps that insist on their own DNS—can defeat the illusion. redir-host leans on real addresses and can feel simpler when debugging classic TCP flows, at the cost of different caching behavior.

When HTTPS stops after the ClientHello and logs show odd SNIs, revisit the dedicated comparison in fake-ip versus redir-host routing fixes and the sniffing notes in SNI log triage. The goal is not to memorize buzzwords but to ensure the domain for files.pythonhosted.org is classified the same way in DNS and in the connection list while pip runs.

Browser-only secure DNS (DoH) can also fork resolution away from your core, making Clash rules appear “ignored.” If that pattern sounds yours, read Chrome and Edge secure DNS versus system proxy before ripping up Python environments.

6. pip.conf knobs: index-url, retries, timeout, and cache hygiene

Routing fixes most stalls, yet pip still exposes useful guardrails. In pip.conf (or inline flags), raise timeout modestly when latency spikes cross the Pacific, and let retries absorb occasional RSTs—defaults are reasonable, not gospel. If you mirror, set index-url and trusted-host deliberately; mismatched TLS expectations between mirror and client create errors unrelated to proxies.

# Excerpt: pip.conf (global or per-user)
[global]
timeout = 120
retries = 5

After a failed mid-download, corrupted entries sometimes linger. python -m pip cache purge is blunt but effective when checksum errors repeat for the same wheel. Combine that with --no-cache-dir only when debugging; in production pipelines you want caching—just not poisoned tarballs from half-closed connections.

Prefer python -m pip over bare pip when multiple interpreters coexist; the shim you invoke might not match the virtual environment you think you activated. That mismatch is infamous in tutorials but still wastes hours when PATH order surprises teams.

7. Log-driven triage checklist

When someone pings you with “PyPI down??” run order matters. First, capture a verbose client trace: python -m pip install -v requests==2.31.0 (pin a small pure-Python release to reduce moving parts). Copy each HTTPS host it touches—not just the index. Second, align those hostnames with live lines in your dashboard connection log or plaintext mihomo log. If the hostname never appears, you are not on the proxy path you believe you are.

Third, compare resolver output: dig +short pypi.org and dig +short files.pythonhosted.org from the same machine while Clash modes toggle. Wildly different answers hint at split DNS or cached fake-ip leakage. Fourth, test naked TLS with curl -vI https://files.pythonhosted.org in the same shell as your exports. If curl succeeds while pip fails, suspect interpreter-specific trust store issues or a stray PIP_INDEX_URL override—not the exit node.

Fifth, when only gigantic wheels fail, look for bandwidth shaping on DIRECT paths. Some ISPs throttle long CDN pulls unless they see an overseas endpoint; routing files.pythonhosted.org through a healthy PROXY group often stabilizes throughput even if small API calls worked fine without it.

8. Quick FAQ

Will a domestic mirror remove the need for foreign rules? Sometimes metadata speeds up, but wheels may still point at PyPI CDNs unless the mirror fully syncs blobs. Keep rules for files.pythonhosted.org until your traces prove zero public artifact hits.

Does poetry, uv, or pipenv change the network story? Different UX, same HTTP reality: they resolve distributions and still fetch bytes from artifact URLs that rhyme with PyPA infrastructure. Point the process at the same proxy variables and keep YAML domains synchronized.

Should I disable certificate verification “just once”? No. Fix the path, trust store, or corporate root instead; disabling verification trains muscle memory that audit teams rightfully hate.

9. Verification checklist

Getting pip, PyPI, and files.pythonhosted.org through one coherent Clash profile saves more than time—it saves your team from superstition about “bad Python versions.” Generic VPN apps can tunnel everything indiscriminately, which frequently breaks on-prem artifact servers and still fails to explain why only large wheel downloads stall; browser-centric proxies rarely expose the logging granularity that mihomo offers for domain-level triage. One-off HTTP forwarding scripts also rot quickly as soon as someone adds a second mirror or CI image layer. A maintained client with transparent YAML, clear connection logs, and predictable updates fits developer workflows where reproducibility matters more than a glossy map animation. When you are ready for installers that stay aligned with the rest of this site’s guidance, use our download hub instead of chasing random forum builds.

→ Download Clash for free and experience the difference

Hand-picked deep-dives on the same topic — practical Clash routing guides in the same category.