Why the CLI Story Is Not the Browser Story
A Chromium tab gives you DevTools, a shared cookie jar, and a reasonably consistent idea of what “the internet” means today. A Gemini CLI binary is smaller on the surface but messier underneath: it may spawn a one-off browser for OAuth, then return to pure TLS toward googleapis.com with keep-alives that last longer than most interactive web navigations. Some builds bundle helpers that still pull scripts or telemetry over static CDN namespaces. Others lean on language runtimes where HTTP stacks honor HTTPS_PROXY in one code path and bypass it in another.
Clash matches each connection independently. That precision is exactly what you want—until your YAML reads like three different opinions about Google AI. One line sends “AI” hostnames to a glossy policy group, another keeps generic Google on DIRECT, and a tracker list accidentally pins gstatic.com somewhere unintended. The CLI does not articulate that tension; it prints a spinner, retries, and eventually surfaces an API timeout string that sends people hunting for new API keys.
The fix is structural: give the whole session one coherent outbound story. The habits in Clash rule routing best practices still apply—especially keeping narrow vendor lines above lazy catch-alls and reviewing rule order whenever a provider update lands.
Typical Failure Modes: OAuth, APIs, and CDNs Out of Sync
Most tickets cluster into a handful of shapes. First, OAuth appears to succeed in the browser, but the CLI never receives a token because a follow-up call to oauth2.googleapis.com or a token service hostname went through a different exit than the browser tab that just authenticated. Second, tokens exist yet model calls stall: generativelanguage.googleapis.com (and related AI platform hosts) ride a lossy path while accounts.google.com was fast. Third, downloads of tiny static objects lag because gstatic.com or sibling CDNs resolve to a geography that does not match the session implied by your proxy exit; the CLI appears frozen until a client-side deadline wins.
Less exotic but equally common: the developer sets HTTPS_PROXY to an address the CLI never consults, so only half of the subsystems use Clash at all. Or TUN is on, but a corporate PAC still steers Google domains unexpectedly, effectively splitting the same binary across two worlds. When you read forum threads complaining that “Gemini is down,” ask which leg actually failed—the error text rarely says.
Hostnames You Should Expect on the Wire
You do not need to memorize every Google subdomain to operate a profile; you do need to recognize families. Account and consent surfaces usually touch google.com and accounts.google.com. API traffic routinely lands on googleapis.com, including Generative Language endpoints and quota dashboards exposed under the same suffix. Static payloads frequently originate from gstatic.com. Downloads, attachments, or signed URLs may appear under googleusercontent.com or other shared buckets.
When Google ships new experiments, you may see short-lived hostnames. Treat those as signals to widen coverage temporarily, capture the exact name, then convert it into a precise DOMAIN line with a comment that records the version you observed. Over time, some of those graduate into suffix rules; others remain one-off quirks best handled case by case.
If you already tuned browser flows, reuse that vocabulary instead of reinventing it: our Google AI Studio split routing article explains how web clients stitch the same namespaces together; keep the CLI on the same policy group names where policy allows.
Terminal Processes, libcurl, and Proxy Environment Variables
Many practitioners export export HTTPS_PROXY=http://127.0.0.1:7890 (port illustrative) before running AI CLIs. That is valid when every code path respects the variable. It fails when a helper uses a custom TLS stack, calls an embedded binary, or resets the environment inside a subprocess. When that happens, Clash will look idle in the UI even though your shell session “has” a proxy because the idle process never consulted it.
Document what you set: HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and especially NO_PROXY exclusions for loopback. Pair those exports with live connection tables so you can prove whether the CLI process actually opened a tunnel through your mixed port. If you prefer system-wide enforcement, TUN captures traffic that env vars miss, at the cost of dealing with Apple or Microsoft extension prompts—see Clash on macOS: TUN versus system proxy for stacking guidance and the TUN deep dive for trade-offs that transcend any single vendor.
For containers and automation hosts, the shape rhymes with bare metal: you still need one routing narrative for the namespaces that participate in auth and inference. Clash with Docker and CLI mixed ports walks related wiring when binaries run inside namespaces that rewrite networks again.
OAuth Callbacks, Loopback, and Accidental Proxy Loops
Browser-based OAuth for a terminal product almost always ends with a loopback callback such as http://127.0.0.1:… Your proxy must not “helpfully” intercept that listener in a way that delays token handoff. A classic foot-gun is routing localhost through a remote exit or rewriting it in a filter list. If the CLI opens a local port for the callback, keep that path short: exclude loopback from upstream proxies, and avoid chaining another local debugging proxy unless you understand the recursion.
When teams share screenshots of “OAuth stuck,” zoom in on timestamps: did the browser finish before the terminal printed an error? If yes, the browser leg succeeded; hunt API or token-exchange hostnames next. If the browser itself cannot load Google’s login assets, broaden CDN coverage first—you may be staring at a static bundle timeout disguised as authentication.
CDN Footprint Even When You “Only” Run a Binary
Operators love to say, “This is not a website; why would gstatic.com matter?” Modern CLI distributions still embed webviews, download policy files, fetch small JSON manifests, or pull crash instrumentation over the same static infrastructure that feeds marketing pages. Those calls are individually small but often gating: until a JavaScript bootstrap completes, the UI shim inside the CLI may refuse to advance.
Treat static suffixes as first-class participants in your split routing plan until logs prove you can narrow them. The debugging loop is rote: add conservative coverage, confirm the timeout disappears, then trim with evidence—not the other way around.
One Policy Group for Coherent Google AI Legs
Name a dedicated group—GOOGLE_AI_CLI or reuse GOOGLE_AI if you already maintain it—and route the namespaces that participate in a session together through that group. The objective is coherence, not maximal proxying. Domestic direct lines, LAN bypass rules, and enterprise VPN prefixes should still precede vendor catches so you do not leak internal address space out a public exit.
Ordering remains the hidden boss: a zealous GEOIP line that starves a narrowly scoped suffix rule is indistinguishable from “Google broke.” After geography basics, place explicit DOMAIN-SUFFIX lines for the authentication, API, and static families you rely on, then fall through to your default MATCH. If you are still selecting a distribution, read choosing the right Clash client with an eye toward readable logs—CLIs fail in ways that reward good telemetries.
Illustrative DOMAIN-SUFFIX Baseline
The fragment below is illustrative. Your subscription may duplicate some suffixes; your region may demand tighter lists; compliance may forbid certain exits altogether. Diff it against your live profile rather than pasting blindly.
Illustrative rules fragment
rules:
- DOMAIN-SUFFIX,google.com,GOOGLE_AI_CLI
- DOMAIN-SUFFIX,googleapis.com,GOOGLE_AI_CLI
- DOMAIN-SUFFIX,gstatic.com,GOOGLE_AI_CLI
- DOMAIN-SUFFIX,googleusercontent.com,GOOGLE_AI_CLI
- GEOIP,CN,DIRECT
- MATCH,DIRECT
Notice the pattern: authentication, API, and common CDN namespaces ride together so token exchange and inference share the same latency envelope. When captures reveal a one-off experiment host, add a temporary DOMAIN line, open an issue in your internal runbook, and decide later whether it deserves promotion to a suffix.
Resist the temptation to sprinkle DOMAIN-KEYWORD,google everywhere. Keyword rules over-capture traffic you cannot explain months later; explicit suffixes scale better for operators who actually read their configs.
Rule Providers, Updates, and Ordering Discipline
Remote rule sets help you stay current—until you cannot fetch them. If provider traffic itself loops through a broken chain, your lists rot quietly and new Google endpoints fall into generic MATCH behavior you did not intend. Watch refresh logs, pin emergency baselines you own, and review diffs when a midnight auto-update coincides with fresh API timeout reports.
Tracker or ad blocklists sometimes classify shared infrastructure in surprising ways. When a spinner appears immediately after a list update, roll forward with intent: temporarily disable the suspect provider on a test machine, reproduce, and restore once you understand the interaction.
DNS, fake-ip, TUN, and When Env Vars Lie
Misaligned DNS is the invisible accomplice of split routing. macOS encrypted DNS, browser DoH, corporate split-horizon, and Clash DNS each believe they own resolution. When fake-ip mapping disagrees with the outbound policy you ultimately select, you observe “instant resolve, endless dial,” which clients usually label as generic timeouts.
Meta cores expose rich DNS knobs; read Clash Meta DNS: nameserver fallback and fake-ip filter until the relationship between nameserver policy, fallback, and filter lists feels boring—that boredom is the goal. The story your resolver tells must match the story your routing table enforces.
When encrypted DNS at the OS fights with Clash, pick a single orchestrator for the test window, document the choice, and revert deliberately. Multiplexing resolvers without a diagram is how teams lose weeks.
Interpreting Timeouts and TLS Stalls in Logs
Treat logs as a sequence diagram. Syn stalls often mean routing or DNS lies; TLS hangs after ClientHello may mean an incompatible exit or middlebox interference; steady throughput followed by resets may mean brittle nodes rather than misguided YAML. Bucket hostnames into google.com (account), googleapis.com (API), and static CDN families; if one bucket consistently diverges from your intended policy, fix the rule—not the model name.
For pattern vocabulary with concrete examples, use connection logs: timeout and TLS patterns. Pair that with the developer-focused login angle in Cursor login and AI timeouts—different product, same lesson about OAuth plus API legs falling out of sync under a proxy.
Verification Checklist Before You Blame the Model
- Confirm you are permitted to use Clash and Gemini tooling on this network.
- Verify system clock skew is negligible; pause HTTPS interception while testing.
- Reproduce once with logging open; copy exact hostnames that overlap the stall window.
- Check each hostname against your effective ruleset—did it hit
GOOGLE_AI_CLIas intended? - Audit rule order for geography catches, tracker lists, or keyword starvation above suffix coverage.
- Align DNS mode with fake-ip and fallback; hunt for instant resolve with no successful TCP.
- Validate
NO_PROXYfor loopback callbacks; remove surprise double proxies. - If env vars look ignored, test TUN after resolving VPN stacking conflicts.
- Confirm remote providers refresh successfully—no silent stale lists.
- Only after local variables are ruled out, rotate nodes or consult Google service dashboards.
Write what changed, when, and why. Future you—and anyone pairing with you—deserves a diff, not folklore.
Frequently Asked Questions
Does API key mode avoid OAuth split entirely?
Keys remove the browser dance only if every dependency honors keys and never falls back to user consent flows. Many teams still see intermittent accounts.google.com traffic for quota, billing, or workspace linking. Keep suffix coverage broad until captures say otherwise.
Why does only streaming or long completions time out?
Long polls and server-sent streams keep connections open far longer than a quick REST ping. Middleboxes, lossy exits, and NAT timers that tolerate short web requests may kill prolonged AI streams. Routing alone cannot fix a dead node, but split paths make the failure look like “Gemini hates me” when the truth is simpler: inconsistent exits plus an aggressive idle timeout.
Is this specific to Gemini CLI?
The pattern generalizes to other Google AI CLIs that combine OAuth, googleapis.com, and static infrastructure. Product names change; the routing lesson does not.
Wrap-Up: One Routing Story for the Whole CLI Session
One-process VPN apps and opaque “accelerators” can hide complexity, but they hide evidence too: when a Gemini CLI session fails, you get a useless spinner instead of a rule hit map. Generic kill-switch VPNs also struggle with split routing granularity—everything rides one tunnel, which solves some problems and creates new API timeout regimes for traffic that should have stayed local. Hand-curated hosts files rot weekly and never track moving CDN edges gracefully.
Clash V.CORE keeps the upside of explicit policies: suffix rules you can read, provider updates you can audit, DNS modes you can align with TUN or mixed ports, and logs that show which leg broke first. That observability is the difference between debugging Google’s distributed terminal surface and guessing from error strings.
→ Download Clash for free and route OAuth, googleapis.com, and CDN legs as one coherent story—then spend your evening shipping prompts, not packet trivia.