Troubleshooting
Split any problem into two questions first: is traffic reaching Chute at all (a takeover problem), and is Chute able to forward it (a forwarding problem)? The live traffic view answers this — open the Dashboard (iOS) or the main window's Traffic tab (Mac) and browse: if nothing appears, Chute is not receiving the traffic; if connections appear but fail, Chute cannot forward them. The two halves have completely different fixes.
This page is for "it doesn't work". When the question is "what is it doing" — reading requests, keeping bodies, changing a response, simulating a failure — start at Network Debugging instead.
Nothing appears: takeover problems
Chute iOS
- The switch refuses to turn on and the Config bar shakes — no configuration is selected. Tap the Config bar, tap a configuration so it shows a checkmark, then Done ("Please select configuration first").
- The VPN permission dialog was declined — toggle again and approve it. If the VPN profile is stuck (the switch flips back immediately), use Reset VPN Configuration in the app's settings; the next start re-creates the profile and re-prompts.
- Another VPN app is connected — iOS runs one VPN tunnel at a time. Disconnect the other app (or disable its on-demand rules, which can silently re-grab the tunnel).
Chute Mac
- System Proxy is on but an app ignores it — many tools (terminal programs in particular) do not honor the system proxy. Point them at Chute's listener explicitly (Copy Shell Export Command in the menu does this for shells), or use Enhanced Mode, which captures traffic at the network layer.
- Enhanced Mode won't start — the network extension or helper needs approval; see Enhanced Mode troubleshooting for the exact System Settings paths, the "System Extension Blocked" case, and the stale-VPN reset.
- Traffic to LAN addresses bypasses Chute by design — check
skip-proxyandtun-excluded-routesin Miscellaneous Options before assuming takeover is broken.
Connections appear but fail: forwarding problems
- Isolate the path. Switch your policy group to
DIRECT: if pages load direct but fail through the proxy, the problem is the proxy server — wrong host/port/credentials/cipher, or the server is down. Run a latency test on the group; a policy that never passes the test while others do names the culprit. - The wrong rule matches. Look at the matched rule of a failing connection in the live traffic view, then re-read the rule evaluation order: rules are evaluated in two passes, so for hostname-based requests a later non-IP rule can match before an earlier IP rule.
no-resolveandFINALplacement are the usual suspects. - DNS answers look wrong. Verify the DNS section: on encrypted DNS, make sure the DoH/DoT server itself is reachable without the proxy; purge the DNS cache after changing servers (iOS Control Panel switch,
flushDNSfrom a script, orDELETE /api/dns/cacheon the HTTP control API). - UDP-dependent apps misbehave — confirm the selected policy supports UDP relay (see the capability matrix in Proxy Policy) and remember Tailscale does not forward ICMP, so
pingthrough an exit node stays silent.
HTTPS decryption doesn't decrypt
- The CA must be installed and trusted — two separate steps on iOS; the second (Settings → General → About → Certificate Trust Settings) is the one everyone misses. See Install and trust the CA certificate.
- The host must match the
[MITM]hostnamelist — only declared hosts are decrypted, and only on port 443 unless a:port/:0suffix says otherwise. - Some apps pin their certificates and will fail while decrypted — exclude their hosts with a
-prefix rather than fighting them. - QUIC/HTTP-3 cannot be decrypted — see
block-quicfor steering compatible clients back to TCP. - On iPhone and Apple TV, decryption is a licensed feature: without a license nothing is decrypted and the MitM switch has no effect — see License and Activation.
Reading the logs
When the sections above don't settle it, the log usually does:
- Raise the log level temporarily:
loglevel = verbose(revert afterwards — verbose is slow). - Chute Mac: the main window's Log tab. Chute iOS: the session log screen, which has a share button in the navigation bar — it hands over every shard of the run. Chute tvOS: the session log screen, with a severity filter above it (All / Notify+ / Warning+ / Fatal) so a remote is enough to narrow it down.
- Any platform: the console's Logs page, or
GET /api/logson the HTTP control API. - Warnings are the interesting lines: unknown policies, rejected options, and unparsable rules are all logged as warnings when the configuration loads.
- The log is split into shards of a few megabytes each. Chute keeps the most recent ones for a run, so the newest file is the end of the story rather than the whole of it — take all of them. (Chute Android keeps this run's log in memory instead, with no shard files on disk.)
- On macOS the files themselves are under
~/Chute/Share/<run id>/— see File Locations (macOS).
Sending a diagnostic bundle
When you need someone else to look, one archive beats six files found through a share sheet and a crash described from memory. The apps distinguish two kinds: a runtime diagnostic bundle, built by the running engine, and an offline diagnostic bundle, built by the app on its own. In both, passwords, tokens, cookies and URL credentials are replaced by <redacted>, and request and response bodies are not included.
Runtime diagnostic bundle — built by the running engine: a redacted copy of the configuration, the engine's health snapshot (including how the previous run ended), this run's notable events, the loaded rules and policies, DNS, traffic, and the log tail (on Android, this run's in-memory log ring, since there are no shards on disk). It needs the tunnel to be running.
- Chute iOS: Control Panel → the last row of the LOCAL PROXY section, Runtime Diagnostic Bundle — always listed, greyed out until the tunnel is connected, and needing no
external-http-controller; tapping it builds the bundle and opens the share sheet - Chute Android: Control Panel → Runtime Diagnostic Bundle, below the HTTP API rows — disabled until the VPN runs
- Chute Mac: menu bar → Save Diagnostic Bundle… — the engine runs inside the app, so this one bundle covers both kinds and works whether the engine is running or stopped
- Chute tvOS: this device has no share sheet or file browser, so the console's Download diagnostic bundle is the only way — scan the QR code in the app and open the Diagnostics page on a device you can send mail from
- Any platform, from the console: the Diagnostics page has a download button, or
POST /api/diagnostics/bundle
Offline diagnostic bundle — built by the app without the engine, so it works when the tunnel is down or was never started: the app's host report (version, device, VPN state, a configuration summary, and the Network / Proxy / Route Table diagnosis pages as text), the previous run's exit marker (reported as "running" if the engine is actually up), and the log files the app can reach.
- Chute iOS: Settings → the DIAGNOSIS section → Offline Diagnostic Bundle
- Chute Android: Settings → the DIAGNOSE section → Save Offline Diagnostic Bundle
- Chute tvOS: Settings → Offline Diagnostic Bundle — the Apple TV builds the bundle and shows a QR code; scan it with a phone on the same network (or open the shown address on a computer) to download the zip — the link works only while that screen is open
- Chute Mac: no separate offline bundle is needed — the menu bar's Save Diagnostic Bundle… works with the engine stopped, and the files it draws on are plain files you can attach directly: the log shards under
~/Chute/Share/<run id>/and the run marker~/Chute/Share/last-run.json— see File Locations (macOS)
The file name says which kind you have: a runtime bundle is diagnostics-<timestamp>.zip, an offline one diagnostics-offline-<timestamp>.zip; both contain a manifest.json whose kind field says the same.
The Previous exit line on the console's Diagnostics page is worth reading before you send anything: Killed for memory means the system reclaimed Chute rather than Chute failing, and that changes what to look for.
Why doesn't my rewrite do anything?
A rewrite rule that never matches has no symptom — nothing happens, which looks exactly like a rule that matched and did nothing visible. The console answers this directly: the Rules page lists every rewrite and mock rule that has fired this run, with a count. The table tracks up to 512 distinct rules; past that it reports how many more fired without being tracked (rewrite_hit_dropped_rules on the API), and is then only a partial view.
- A rule that is missing from that list has never matched — as long as the table is not reporting untracked rules. Check the pattern against the URL forms in URL Rewrite; Header Rewrite matches the whole URL rather than a substring.
- A rule that fired but changed nothing you can see is a different problem — open the connection in the console and read its Rewrites applied rows, which name the rule in its own words.
- Rules only see HTTPS traffic when HTTPS decryption is on for that host.