Running Antigravity Behind a Proxy — Connection Design for Corporate Networks, SSL Inspection, and Hotel Wi-Fi
Keep Antigravity working behind corporate proxies, SSL inspection, and hotel Wi-Fi: layer-by-layer diagnosis, certificate trust, NO_PROXY design, and connection profiles with a local LLM fallback.
In 2025, in a hotel room where I was staying for an overseas exhibition, the Antigravity agent that had been running smoothly for weeks suddenly went quiet. Chat requests hung at "sending" and never came back, while tab completion still fired occasionally, as if nothing were wrong. It took me half a day to find the culprit: a transparent proxy sitting on the hotel's line, quietly rewriting part of the HTTPS traffic and selectively killing the streaming connections.
What makes connection trouble so frustrating is that "the network is bad" never describes it accurately. Antigravity keeps several very different kinds of connections open at once — authentication, model APIs, agent tool execution, extension updates — and a proxy usually breaks only some of them. The result is a half-broken state that resists diagnosis: completion works but the agent is dead, or everything works except sign-in.
Before going independent in 2005 I spent years at NTT Data, developing inside a large corporate network, negotiating daily with proxies and auth servers. That instinct has not changed much; what has changed is that I now maintain my indie apps while moving between exhibition venues and hotels around the world. What follows is the connection design that has slowly taken shape from both of those lives — a way to keep Antigravity working on networks that do not want to cooperate.
Antigravity's Traffic Is Not One Pipe — Reverse-Mapping Symptoms to Layers
From what I have observed, Antigravity talks to the outside world through five distinct paths.
Authentication: Google account sign-in and the OAuth callback. This goes through your default browser, not the IDE, so if the browser and the IDE see different proxy settings, sign-in alone will fail
Model API: traffic to Gemini and other cloud models. Responses arrive as streams over long-lived HTTPS connections — exactly the kind of connection that middleboxes love to kill first
Agent runtime: git, npm, curl and other tools the agent spawns as child processes. These never read the IDE's settings; they only read environment variables
Extensions and updates: the marketplace and auto-update checks. A failure here won't stop today's work, but stale extensions eventually cause their own confusing bugs
Local LLMs: localhost traffic to Ollama or LM Studio. This is the one path that must not go through the proxy
Keep these five in mind and you can reverse-map symptoms to layers.
Only sign-in fails → authentication; check whether the browser uses different proxy settings
Chat hangs at "sending" → model API; the stream is probably being cut by the proxy
Responses start, then die midway → the proxy's idle timeout; longer generations get cut more often
Only the agent's command execution fails → environment variables are not reaching the IDE
A local model that worked on a direct connection disappears → a NO_PROXY gap
Absolutely nothing connects → an unauthenticated captive portal, or DNS
After building this mapping, the time I spend identifying the cause on an unfamiliar network shrank from half a day to roughly ten minutes. The next section is what those ten minutes look like.
The First Ten Minutes — Five Commands to Locate Yourself
Before touching any settings, find out which layer you are stuck on. If you change settings blindly, you will not know why things broke — or why they started working again. Observation first, changes second. It sounds pedantic, but it is the single biggest factor in recovery time.
# (1) Is a proxy defined in the environment? — find out who claims to be your proxyenv | grep -i proxy# (2) Are you trapped behind a captive portal? — 204 means you pass through cleanlycurl -s -o /dev/null -w "%{http_code}\n" --max-time 5 \ http://captive.apple.com/hotspot-detect.html# (3) Does plain HTTPS reach the model API — and who is issuing the certificate?curl -sv --max-time 10 https://generativelanguage.googleapis.com -o /dev/null 2>&1 \ | grep -E "issuer:|HTTP/"# (4) Does it work when you explicitly go through the proxy? (use your company's value)curl -sv --max-time 10 -x http://proxy.example.com:8080 \ https://generativelanguage.googleapis.com -o /dev/null 2>&1 \ | grep -E "CONNECT|issuer:|HTTP/"# (5) Is the local LLM alive? — this should answer directly, proxy or notcurl -s --max-time 3 http://127.0.0.1:11434/api/tags | head -c 120
Check (2) uses the plain HTTP endpoint Apple relies on for captive portal detection. Anything other than 204 — a 302, or a 200 that returns HTML — means the line is not broken; you simply have not clicked through the hotel's consent page yet. Open a browser and do that first.
In the output of (3), the first thing I look at is the issuer line.
# A healthy direct connection — issued by a public CAissuer: C=US; O=Google Trust Services; CN=WR2# Behind an SSL-inspecting proxy — the issuer turns into an unfamiliar corporate CAissuer: O=ExampleCorp Security; CN=ExampleCorp TLS Inspection CA
If the issuer is a name you do not recognize, that network is decrypting and inspecting HTTPS. The certificate work described below becomes mandatory; no amount of proxy configuration will fix things if you skip it.
If (5) does not answer, there are two possibilities: a problem on the Ollama side, or your proxy swallowing localhost traffic. For the Ollama-side diagnosis I wrote a separate piece, Diagnosing Ollama Connection and Recognition Failures in Antigravity; here, just remember that the more proxy variables you have set, the more you should suspect (5).
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Pinpoint why Antigravity goes silent behind a corporate proxy by isolating the failure across five distinct connection layers, instead of changing settings blindly
✦Implement the safe-side fixes for SSL inspection, authenticated proxies, and captive portals — NODE_EXTRA_CA_CERTS, a local relay, and a deliberate NO_PROXY list
✦Build a connection-profile workflow with a local LLM fallback so development keeps moving on hotel Wi-Fi, tethering, or no network at all
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Baseline Proxy Settings — Why settings.json and Environment Variables Must Both Be Set
The Antigravity application itself (the Chromium layer) reads settings.json and the OS proxy configuration. Start there.
// settings.json — the three basic proxy settings{ // Replace with the proxy value your company distributes "http.proxy": "http://proxy.example.com:8080", // I make settings.json the single source of truth to avoid double management "http.proxySupport": "override", // Keep certificate verification ON (the next section explains why) "http.proxyStrictSSL": true}
Meanwhile, the tools the agent spawns as child processes — git, npm, curl — never read settings.json. They only read environment variables. When completion works but the agent's tool execution keeps failing, this is almost always the hole.
# ~/.zshenv — proxy settings for CLI tools and child processesexport HTTPS_PROXY="http://proxy.example.com:8080"export HTTP_PROXY="$HTTPS_PROXY"# Some tools still read only the lowercase variants, so define bothexport https_proxy="$HTTPS_PROXY"export http_proxy="$HTTP_PROXY"export NO_PROXY="localhost,127.0.0.1,::1"export no_proxy="$NO_PROXY"
There is a macOS-specific trap here. GUI apps launched from the Dock or Spotlight do not read your shell's configuration files. The classic symptom — it works when launched from a terminal, goes silent when launched from the Dock — is exactly this. I lost a few days to "it worked yesterday but not today" before realizing the difference was simply how I had launched the app.
To make environment variables reach GUI launches as well, use launchctl.
# Make the variables visible to Dock/Spotlight launches of Antigravity — macOSlaunchctl setenv HTTPS_PROXY "http://proxy.example.com:8080"launchctl setenv HTTP_PROXY "http://proxy.example.com:8080"launchctl setenv NO_PROXY "localhost,127.0.0.1,::1"# Takes effect only after a full quit and relaunch (Cmd+Q, then reopen)
You might wonder whether one of the two — settings.json or environment variables — would be enough. I always set both. A state where the IDE connects but every agent tool call dies, or the reverse, costs far more diagnostic time than a clean total failure ever does.
SSL-Inspecting Proxies — Add Trust Instead of Disabling Verification
If the issuer in check (3) turned out to be a corporate CA, you will see errors like self signed certificate in certificate chain or UNABLE_TO_VERIFY_LEAF_SIGNATURE until you deal with it. The fastest band-aid is "http.proxyStrictSSL": false — and I am firmly against making that permanent.
The reason is simple: what Antigravity sends to the model API is your source code. Disabling verification means you can no longer tell who is decrypting that traffic. As a way of trusting only your company's proxy, it is far too blunt — the same setting will happily trust a shady hotel network too.
The proper fix is to add the inspecting proxy's CA certificate to your trusted roots. If your IT department distributes one, use it; if not, you can extract it yourself.
# (1) Extract the certificate chain the proxy is injectingmkdir -p ~/certsopenssl s_client -connect generativelanguage.googleapis.com:443 \ -showcerts </dev/null 2>/dev/null \ | awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/' > ~/certs/corp-proxy-chain.pem# (2) Verify whose certificate this is — the issuer should show your corporate CAopenssl x509 -in ~/certs/corp-proxy-chain.pem -noout -subject -issuer# Example output:# subject= /O=ExampleCorp Security/CN=ExampleCorp TLS Inspection CA# issuer= /O=ExampleCorp Security/CN=ExampleCorp Root CA
Then distribute that trust to each layer.
# (3) Trust for Node-based runtimes (agent, extensions)# NODE_EXTRA_CA_CERTS *adds to* the default roots — it does not replace themexport NODE_EXTRA_CA_CERTS="$HOME/certs/corp-proxy-chain.pem"# (4) Give the same trust to the tools the agent spawnsgit config --global http.sslCAInfo "$HOME/certs/corp-proxy-chain.pem"npm config set cafile "$HOME/certs/corp-proxy-chain.pem"# (5) For the IDE itself (Chromium layer), go through the keychain — macOSsudo security add-trusted-cert -d -r trustRoot \ -k /Library/Keychains/System.keychain ~/certs/corp-proxy-chain.pem
I prefer NODE_EXTRA_CA_CERTS for two reasons. First, it adds rather than replaces, so trust in public CAs stays intact. Second, it is an environment variable, so removing it is a one-liner: leave the corporate network, unset it, and the trust in the inspection CA disappears with it. The trustRoot entry deserves the same discipline — delete it when the assignment or the trip ends, so no leftover trust quietly follows you home.
Authenticated Proxies — A Relay Setup That Keeps Credentials Out of Plaintext
When the corporate proxy requires user authentication, the first idea that comes to mind is embedding credentials in the URL.
# It works, but I do not recommend itexport HTTPS_PROXY="http://masaki:P@ssw0rd@proxy.example.com:8080"
The problem: your password now lives in plaintext in env output, shell history, the process list, and your dotfiles. I sync my configuration files through Git across two Macs, so adopting this style would put me one careless commit away from pushing credentials to a repository. Years of handling AdMob and payment keys for my app business taught me to reject any design that multiplies plaintext locations, before convenience gets a vote.
What I use instead is a local relay proxy, one hop in front.
Antigravity → (no auth) → local relay at 127.0.0.1:3128 → (NTLM/Basic auth) → corporate proxy
A lightweight tool like px works well as the relay. Only the relay process knows the credentials; they vanish from Antigravity's configuration entirely.
// settings.json — the IDE holds no credentials at all{ "http.proxy": "http://127.0.0.1:3128", "http.proxySupport": "override"}
# Point the environment at the relay too — the password is written nowhereexport HTTPS_PROXY="http://127.0.0.1:3128"export HTTP_PROXY="$HTTPS_PROXY"
The payoff is that credentials live in exactly one place: the relay's configuration. When the password rotates, there is one thing to change, and Antigravity, git, and npm are untouched. "One secret, many references" — the same principle I follow for API key management in my apps.
Designing NO_PROXY — Keeping Ollama and LM Studio Off the Proxy, Reliably
The most common piece of collateral damage right after configuring a proxy: "the local LLM that worked yesterday has disappeared." Once HTTPS_PROXY is defined, even traffic to 127.0.0.1 gets forwarded to the proxy unless you explicitly exclude it. To the corporate proxy, localhost means itself — so your requests will never reach the Ollama instance running on your own Mac.
NO_PROXY syntax carries an implementation-difference trap.
Some tools do not interpret CIDR notation (192.168.0.0/16)
Some implementations ignore port-qualified entries (127.0.0.1:11434)
Wildcard handling (*.local) differs from tool to tool
So I lean on explicit enumeration of hosts and IPs, and avoid clever syntax altogether.
# Exclude local inference from the proxy — enumerate explicitly, skip clever syntaxexport NO_PROXY="localhost,127.0.0.1,::1,192.168.11.20,mac-studio.example-tail.ts.net"export no_proxy="$NO_PROXY"# Verify: can you still reach Ollama directly with proxy variables set?curl -s --max-time 3 http://127.0.0.1:11434/api/tags | python3 -m json.tool | head -6# Expected output (a model list means NO_PROXY is working):# {# "models": [# {# "name": "gemma4:27b",# ...
The 192.168.11.20 and ts.net entries are from my own setup — a Mac Studio at home serving as an inference machine, reached from outside via Tailscale. If you keep an inference box on your LAN or VPN, remember to enumerate it as well. For LM Studio-specific symptoms, the isolation steps in LM Studio Models Not Visible or Disconnecting in Antigravity — Ports, CORS, and Model Visibility should help.
You may be tempted by NO_PROXY=* — exclude everything. On a proxy-mandatory network, that kills every external API at once. Express your actual intent in the NO_PROXY list — local goes direct, external goes through the proxy — and the configuration keeps meaning the same thing no matter which network you carry it to.
The Reality of Networks on the Road — Captive Portals, Tethering, and Profile Switching
Now for the part about working from hotels and venues. I have run my app business as an indie developer for over a decade, and these past few years the exhibitions have kept me moving abroad for weeks at a time. The user reviews and crash reports behind 50 million cumulative downloads do not pause while I travel, so Antigravity inevitably gets opened on a lot of hotel connections.
What that experience has taught me: networks on the road behave worse than corporate proxies.
While a captive portal is unauthenticated, all HTTPS gets hijacked toward the consent page and turns into certificate errors. Nothing is broken — you just have not seen the consent screen yet
Transparent proxies in budget hotels and venues sometimes buffer streaming responses. If chat answers arrive as one big lump after a long silence, that is it — no client setting fixes it, and switching lines is the correct move
Downloading models over metered tethering is an accident waiting to happen. I once pulled Gemma 4's 27B over metered tethering between exhibition-setup sessions and burned several days' worth of data in a single evening. The lesson, learned that night: run ollama pull while you still have a fixed line
This life eventually produced a small set of shell functions for switching and checking network state in one command.
# ~/.zshrc — connection profile switching and a quick locatorproxy_on() { # entering a corporate/venue proxy network export HTTPS_PROXY="http://proxy.example.com:8080" export HTTP_PROXY="$HTTPS_PROXY" https_proxy="$HTTPS_PROXY" http_proxy="$HTTPS_PROXY" export NO_PROXY="localhost,127.0.0.1,::1" no_proxy="localhost,127.0.0.1,::1" echo "proxy ON: $HTTPS_PROXY"}proxy_off() { # back to direct connection or tethering unset HTTPS_PROXY HTTP_PROXY https_proxy http_proxy NO_PROXY no_proxy launchctl unsetenv HTTPS_PROXY 2>/dev/null launchctl unsetenv HTTP_PROXY 2>/dev/null echo "proxy OFF"}net_check() { # where am I right now, in three lines echo "captive: $(curl -s -o /dev/null -w '%{http_code}' --max-time 5 http://captive.apple.com/hotspot-detect.html)" echo "proxy: ${HTTPS_PROXY:-none}" echo "ollama: $(curl -s --max-time 2 http://127.0.0.1:11434/api/tags >/dev/null 2>&1 && echo OK || echo NG)"}
From my own failures and from readers' questions, in descending order of how often they recur.
proxyStrictSSL: false survives for a year: the classic pattern — a business-trip band-aid becomes permanent configuration. Using it as a band-aid is fine; just pair it with an undo step. I put "restore strictSSL" on the calendar the same day I change it
Variables written only in .zshrc never reach GUI launches: Dock-launched apps do not read shell rc files. Either pair them with launchctl setenv, or commit to always launching from a terminal — pick one and stop wavering
Cramming CIDR, ports, and wildcards into NO_PROXY: produces the worst possible middle state, where the list works for some tools and silently fails for others. Explicit enumeration is boring and correct
Hardcoding user:pass@ in settings.json and letting Settings Sync pick it up: your credentials get replicated to every synced machine and the sync service itself. Either exclude proxy settings from sync, or move to the relay setup so there is nothing secret in the settings at all
Misdiagnosing an unauthenticated captive portal as a certificate problem: start fiddling with NODE_EXTRA_CA_CERTS over a hotel error page, and things will still be broken when you get home. Fix the diagnostic order — captive → proxy → issuer — and this misdiagnosis becomes structurally impossible
Works at the office, fails at home: usually a leftover launchctl setenv proxy still pointing at an unreachable host. This is exactly why proxy_off includes launchctl unsetenv
Closing — Connections Are Something You Design, Not Something That Breaks
Proxy handling tends to become reactive — something you investigate only when things stop working. But for a way of working that assumes movement and changing environments, treating the connection layer itself as a design target turned out to be far less effort in total. Reverse-map symptoms to layers, decide where each layer's configuration lives — settings.json, environment variables, certificates, NO_PROXY — and any unfamiliar network tells you where you stand within ten minutes.
The next time you change networks, run the three lines of net_check before touching a single setting. Once knowing where you are becomes a habit, the proxy stops being an opponent and becomes just another precondition. If you also build and ship things from wherever life takes you, I hope this note saves you an evening or two.
Share
Thank You for Reading
Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.