1. Symptoms that point to routing—not a broken tap

Classic presentations include brew install parking on an innocuous line such as “Downloading https://github.com/…” or quietly burning CPU while Ruby waits on curl. Sometimes brew update succeeds partially: local taps refresh while remote metadata never finishes. Kernel panics and corrupted disks are rare; more often you simply stare at partial logs until patience expires.

Because macOS separates GUI networking defaults from what an interactive shell inherits, it is easy to misattribute the stall. You might assume Homebrew reads “system proxy” the same way Safari does. In practice brew inherits your Terminal environment and curl defaults—unless you explicitly inject proxies or capture packets with TUN.

Before rewriting taps or reinstalling the Command Line Tools, capture evidence: keep Clash’s connection panel open, reproduce once with verbose brew output, and note hostnames that flip between proxied and DIRECT policies. That thirty-second habit prevents hours of folklore about “GitHub being down” when only one CDN class missed your split configuration.

2. The Homebrew download chain is wider than github.com

Most engineers mentally bucket everything as “GitHub,” yet brew touches multiple hostname families in a single install. Formula definitions may reference archives that live on GitHub Releases; curl follows redirects onto high-throughput object storage domains that do not share the same suffix as the repository browser URL you pasted into chat.

Modern releases frequently resolve through objects.githubusercontent.com and adjacent GitHub asset domains. Static marketing pages might load while object storage stalls because your rule list proxies github.com but leaves storage hosts on a congested DIRECT path. Likewise, raw attachment mirrors under githubusercontent.com remain common for badges, scripts, and auxiliary payloads.

Apple Silicon and Intel bottles add another axis: binary mirrors and relocation logic can shift downloads across domains depending on architecture detection and formula maintainer choices. When routing policies lag behind those hops, brew displays the same user-facing symptom—a spinner—even though each failure mode differs underneath.

If your mental model stops at “allow GitHub,” you will keep seeing intermittent stalls whenever GitHub shifts asset delivery to a CDN hostname your YAML never named. Clash rewards explicit suffix coverage verified against live logs rather than assumptions copied from last year’s gist.

3. Why formulae.brew.sh matters before any tarball moves

The JSON catalog exposed at formulae.brew.sh backs much of what brew considers “online metadata.” When that surface loads slowly or resets TLS midway, operations that precede actual Release downloads—like resolving bottle URLs—appear hung even though GitHub itself might be reachable.

Treat that hostname as first-class in your split policy alongside GitHub. Developers sometimes omit it because tutorials obsess over git clones alone; brew’s HTTP API calls deserve the same routing discipline as large binaries.

Enterprise intercepting proxies add wrinkles: if formulae traffic must traverse inspection appliances while Release downloads should exit elsewhere, naive single-tunnel VPNs hide the distinction and amplify sporadic failures. Evidence-first tuning with separate groups for metadata APIs versus bulk CDN pulls mirrors how operators stabilize Copilot-sized stacks—only here the consumer is brew instead of an IDE extension.

Tip: Open two terminals—one with proxies exported and one without—and compare curl -I https://formulae.brew.sh/api/formula/hello.json latency. Orders-of-magnitude gaps usually reveal missing env vars rather than brew bugs.

4. Confirm whether brew actually sees your command-line proxy

Our dedicated walkthrough on shell exports covers the mechanics in depth: read Route macOS Terminal Through Clash when you need copy-ready HTTPS_PROXY, ALL_PROXY, and zsh helpers aimed at the mixed port.

Summarizing the essentials for brew: export uppercase proxy variables in the same shell session before invoking brew, verify with env | grep -i proxy, and remember mixed HTTP listeners expect values like http://127.0.0.1:7890 even when fetching HTTPS targets—curl upgrades via CONNECT automatically.

If you toggle Clash Verge Rev’s system proxy but skip exports, browsers win while brew loses. Conversely, exporting proxies without starting Clash yields immediate connection failures—always sanity-check with curl --proxy … https://example.com before blaming upstream mirrors.

Advanced users sometimes rely on Git’s proxy configuration or wrapper scripts; brew still shells out to curl with inherited environment variables in common setups. Keeping ONE authoritative approach—either consistent exports or TUN—reduces mystery regressions after reboots.

5. Split-rule buckets that stabilize GitHub Releases traffic

Once logs prove which suffixes stall, encode them explicitly ahead of broad GEOIP or domestic DIRECT shortcuts. The snippet below is illustrative; merge it thoughtfully with your provider’s baseline rules so you do not strand unrelated traffic.

# Example — replace PROXY with your policy group name
rules:
  - DOMAIN-SUFFIX,formulae.brew.sh,PROXY
  - DOMAIN-SUFFIX,brew.sh,PROXY
  - DOMAIN-SUFFIX,github.com,PROXY
  - DOMAIN-SUFFIX,githubusercontent.com,PROXY
  - DOMAIN-SUFFIX,githubassets.com,PROXY
  - DOMAIN-SUFFIX,github.io,PROXY
  - DOMAIN-SUFFIX,objects.githubusercontent.com,PROXY
  - DOMAIN-SUFFIX,ghcr.io,PROXY

Why include container registry suffixes? Some formulae pull assets or dependencies via GHCR-related flows; partial coverage leaves narrow failures that resemble flaky taps. Validate additions against your connection trace rather than blindly trusting community dumps.

When you require finer steering, declare sibling proxy groups—one tuned for interactive APIs and another for long CDN pulls—and assign suffix families accordingly. Declare groups before referencing them; Mihomo-class cores fail fast on typos, which is preferable to silently ignoring missing policies.

BucketRepresentative suffixesRouting note
Brew metadataformulae.brew.sh, brew.shSmall JSON payloads; latency spikes here pause brew before large downloads start.
GitHub browsing APIgithub.comInteractive HTML/API surfaces; not sufficient alone for Release binaries.
User contentgithubusercontent.comRaw files and attachments frequently bypass the main domain’s caching behavior.
Static assetsgithubassets.comMarketing and supporting asset CDNs used alongside repositories.
Release objectsobjects.githubusercontent.comLarge tarball and checksum downloads—common choke point when rules omit CDN coverage.
Container pullsghcr.ioUseful when formulae or builds reference GHCR layers.

6. When to escalate from exports to TUN mode

Environment variables cover most curl-backed workflows, yet stubborn binaries spawned indirectly—helpers, background tasks, or wrappers—sometimes ignore inherited shells. TUN mode pushes interception closer to the operating system so fewer processes can silently bypass Clash.

Enable TUN using our Clash TUN mode guide, then revisit brew installs while watching whether previously DIRECT rows now attach to your chosen policy. Expect occasional friction with other VPN clients or corporate split tunnels; temporarily disable competing overlays while isolating brew behavior.

TUN is heavier than typed exports, but it mirrors how desktop-class Mihomo clients shine: transparent steering without scripting every language ecosystem. Reserve it for laptops where developers juggle brew, language servers, docker-style VMs, and editors that each reinterpret proxies differently.

Warning: Leaving TUN enabled during airline captive portals or hotel Wi-Fi checks can hijack traffic unintentionally. Pause Clash before authenticating, then resume once you have full upstream connectivity.

7. DNS, fake-ip, and why stalls masquerade as TLS timeouts

Mihomo’s DNS stack interacts with rule evaluation. When fake-ip answers diverge from what curl expects—or corporate split DNS returns alternates—TLS handshakes retry until brew appears frozen. Align resolver policies with documented core syntax; targeted suffix forwarding for GitHub and Brew domains often silences ghost failures.

If you bounce between office Ethernet and tethered LTE, flush stale resolver caches mentally by restarting Clash after network changes. Logs showing alternating DIRECT and proxied rows for the same formula frequently trace back to resolver churn rather than bad nodes.

Pair DNS fixes with conservative node selection for long downloads: jittery auto selectors may restart streams mid-transfer on GitHub Releases, amplifying perceived hangs even when routing labels look correct.

8. Practical diagnostics beyond staring at the spinner

Run brew install -v formula_name during investigations; verbosity exposes curl arguments you can replay manually. For curl-level TLS chatter you may temporarily export official diagnostics such as HOMEBREW_CURL_VERBOSE=1, then unset them—verbose streams have historically interfered with niche mirror parsers, so escalate only after plain -v stops short.

Copy the exact HTTPS URL brew prints and fetch it with standalone curl using the same proxy flags your shell exports. If standalone curl succeeds instantly while brew fails, inspect Ruby-level SSL stores or outdated certificates inside your toolchain before rewriting network rules.

When suspicion lands on GitHub-wide outages, cross-check with a browser fetch or curl -I against both github.com and objects.githubusercontent.com. Divergent outcomes validate split-rule hypotheses; identical failures across buckets suggest upstream downtime or local inspection appliances.

Document working combinations—DNS mode, rule ordering, proxy group names—in your dotfiles repo. Future macOS upgrades reorder network stacks often enough that tribal knowledge saves onboarding time for teammates who inherit your laptop image.

9. Mirrors, HOMEBREW_BOTTLE_DOMAIN, and when they help

Mirrors relocate bottle binaries to geographically closer edges. They can accelerate installs once routing works end-to-end, but they do not repair partial GitHub coverage or broken TLS trust stores.

If your employer publishes an approved bottle mirror, point HOMEBREW_BOTTLE_DOMAIN only after verifying authenticity—supply-chain vigilance matters as much as throughput. When mirrors lag upstream signatures, brew legitimately refuses installs; do not disable verification to silence errors.

Combine mirrors with the routing discipline above instead of treating them as a silver bullet. Mirrors complement Clash; they rarely replace accurate suffix rules for Release archives living outside mirror ecosystems.

10. How this differs from GitHub Copilot split guides

Our GitHub Copilot routing article focuses on Microsoft login surfaces, IDE telemetry, and models.github.ai. Those paths overlap GitHub hostnames partially yet emphasize OAuth longevity and extension subprocess quirks.

Homebrew stresses repeatable curl orchestration, formula JSON from formulae.brew.sh, bottle relocation, and Release-backed tarballs—telemetry stacks differ even when both say “GitHub” in casual conversation. Borrow suffix ideas, still reconcile each list against brew-specific traces rather than pasting IDE-centric YAML wholesale.

11. Using Clash Verge Rev as your observability cockpit

Desktop GUIs such as Clash Verge Rev expose live flows beside YAML editors—ideal when you filter for “github” substrings during a verbose brew run. Unexpected DIRECT rows signal precedence bugs; duplicated proxy hops may hint at nested wrappers.

Newcomers should finish Clash Verge Rev setup before deep dives so subscriptions, cores, and inbound ports remain stable while you iterate DNS and rule ordering.

12. FAQ: quick answers when teammates ping you

Does enabling macOS system proxy fix brew automatically?

Sometimes—not always. curl respects exported variables; system-wide toggles alone may skip your shell. Confirm with environment dumps rather than assumptions.

Why did brew work at home but fail in hotels?

Captive portals, DNS hijacks, and aggressive ISP buffering disrupt long GitHub downloads. Authenticate Wi-Fi fully, pause TUN if needed, then retry.

Are Apple Silicon bottles routed differently?

Architecture selects bottle URLs, yet DNS and CDN steering still follow hostnames. Split rules remain mandatory regardless of chip generation.

13. Summary

Homebrew on macOS is rarely “broken” when installs crawl—it is usually telling you that one GitHub CDN strand still rides DIRECT while your browser rides Clash. Cover GitHub Releases, objects.githubusercontent.com, sibling GitHub asset domains, and formulae.brew.sh; align command-line proxy exports or TUN; order split rules ahead of broad bypasses; and verify with verbose curl before rewriting taps.

Compared with blunt full-tunnel VPN apps that hide hostname visibility—or brittle hosts-file hacks that rot after every CDN migration—a Mihomo-powered client pairs adjustable GitHub CDN steering with readable logs. Generic VPNs often optimize browsing latency while leaving developer-oriented object storage on unpredictable paths, and static rule dumps rarely explain why only brew struggles. Clash-style policies let you mirror how Homebrew actually resolves hosts, refresh suffix lists when traces change, and keep observability beside the YAML you edit daily.

If you want that workflow without bouncing between five troubleshooting tabs, grab the maintained desktop build and keep downloads predictable alongside Safari. → Download Clash for free and experience the difference

Back to blog