Misc Options
[General]
ipv6 = true
loglevel = notify
skip-proxy = 127.0.0.1, 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 100.64.0.0/10, localhost, *.local
tun-excluded-routes = 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12
tun-included-routes = 192.168.1.12/32
Common Options
Enable full IPv6 support (Default: true)
ipv6 = true
loglevel (Default: warning)
loglevel = notify
One of none, fatal, warning, notify, info or verbose. It's not recommended to enable verbose in daily use because this will slow down the performance significantly.
skip-proxy
skip-proxy = 127.0.0.1, 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 100.64.0.0/10, localhost, *.local
In iOS version, this option forces connections to these domain/IP ranges to be handled by Chute TUN, instead of Chute proxy. In macOS version, these settings will be applied to system when "Set as System Proxy" is enabled. This option is used to fix compatibility problems with some apps.
- To specify a single domain, enter the domain name - for example, apple.com.
- To specify all websites on a domain, use an asterisk before the domain name - for example, *apple.com.
- To specify a specific part of a domain, specify each part - for example, store.apple.com.
- To specify hosts or networks by IP addresses, enter a specific IP address such as 192.168.2.11 or an address range, such as 192.168.2.* or 192.168.2.0/24.
Notice: If you enter an IP address or address range, you will only be able to bypass the proxy when you connect to that host using that address, not when you connect to the host by a domain name that resolves to that address.
Proxy Server Listening
interface = 127.0.0.1
port = 8118
socks-interface = 127.0.0.1
socks-port = 8119
interface / port control the listening address and port of the HTTP proxy server (default 127.0.0.1:8118). socks-interface / socks-port control the SOCKS5 proxy server (default 127.0.0.1:8119).
Compatibility aliases:
doh-serveris accepted as an alias ofdoh;http-listen/socks5-listen(e.g.0.0.0.0:6152,[::]:6153, or a bare port) are mapped onto the interface/port settings above. A wildcard listen host (0.0.0.0,::, or*) also setsallow-wifi-access = true.
Inbound Proxy Authentication
http-auth = username:password
Requires clients to authenticate before using Chute's HTTP and SOCKS5 proxy servers. The line may be repeated to allow multiple credentials.
Bypass System Requests
bypass-system = true
When enabled, requests sent by system processes bypass Chute processing.
Default:
true.
Always Real IP
always-real-ip = *.example.com, tracker.example.org
When Chute serves fake IP addresses for hijacked DNS queries (see hijack-dns), hostnames matching this comma-separated list are always answered with their real resolved IP addresses instead. Wildcards are supported.
Interrupt Existing Connections
interrupt-exist-connections = true
When enabled, changing the selected policy in any policy group (via URL Test, Fallback, Load Balance, SSID, or manual selection) will gracefully tear down existing connections that were using the old policy. This ensures connections use the newly selected proxy immediately rather than lingering on the old one.
Each affected connection is closed gracefully with a 3-second timeout before being force-closed.
Default:
false. This is a global setting — it affects all policy groups.
Network Framework (macOS / tvOS)
network-framework = true
Enable Apple Network.framework for outbound connections. Using Network.framework can provide better performance and modern TLS stack integration on supported platforms.
Default:
trueon macOS,falseon iOS and tvOS.
Exclude Simple Hostnames
exclude-simple-hostnames = true
When enabled, requests to simple hostnames (single-label names without a dot, e.g. localhost) bypass proxy rules and are resolved locally. This helps avoid unnecessary DNS lookups for local network names.
On Chute Mac, a one-time migration enables this option by default on first run; an explicit user off setting is respected.
Default:
false.
Disable Database Record
disable-db-record = true
When enabled, Chute stops writing traffic records to the local database. This can improve performance and reduce storage usage, but traffic history will not be available in Chute Dashboard.
Default:
false.
Menu Bar Speed Display (Mac Only)
menu-bar-show-speed = true
When enabled, Chute Mac shows the current upload and download speed in the menu bar.
Default:
false.
Hijack Other DNS Servers
hijack-dns = 8.8.8.8:53
By default, Chute only returns fake IP addresses for DNS queries sent to Chute DNS address (198.18.0.2). Queries which are sent to standard DNS will be simply forwarded.
Some devices or softwares always use a hardcode DNS server. (For example, Google Speakers always use 8.8.8.8). You may use this option to hijack the query to get a fake address.
You may usehijack-dns = *:53to hijack all DNS queries.
Excluded Routes
tun-excluded-routes = 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12
Chute VIF can only process TCP and UDP protocols. Use this option to bypass specific IP ranges to allow all traffic to pass through.
Notice: This option only works for the VIF (utun) type of Enhanced Mode — it has no effect when using the PacketTunnel VPN type. Requests handled by Chute Proxy Server will not be affected. Combine 'skip-proxy' and 'tun-excluded-routes' to make sure that certain HTTP traffic bypasses Chute.
This option might cause a system error ENOMEM (Cannot allocate memory). It seems a bug in iOS system. Please do not use this option if possible.
Included Routes
tun-included-routes = 192.168.1.12/32
By default, the Chute VIF interface will declare itself as the default route. But since the Wi-Fi interface has a smaller route, some traffic may not go through the Chute VIF interface. Use this option to add a smaller route.
Note: This option only works for the VIF (utun) type of Enhanced Mode — it has no effect when using the PacketTunnel VPN type.
Protocol Sniffing
sniffing-enabled = true
sniffing-timeout = 100
Chute can detect the actual protocol of a connection by inspecting the initial bytes. This enables rules like PROTOCOL,TLS,Proxy to work correctly even for non-HTTP inbound connections.
sniffing-enabled (Default: false)
sniffing-enabled = true
Enable protocol detection for TCP connections.
sniffing-timeout (Default: 100ms)
sniffing-timeout = 200
Maximum time in milliseconds to wait for the initial data to determine the protocol. Increase this value if protocol detection fails on slow connections.
Block QUIC
block-quic = auto
Controls whether Chute rejects detected QUIC traffic, including HTTP/3. QUIC runs over UDP and cannot be decrypted by Chute's HTTP MitM. Rejecting QUIC can prompt compatible clients to retry over TCP, allowing TCP-based proxy handling and, when configured, HTTPS decryption to apply.
| Value | Behavior |
|---|---|
off |
Do not apply global QUIC blocking (default). Detected QUIC traffic follows the normal routing rules. |
on |
Reject every detected QUIC flow, including traffic routed to DIRECT. |
auto |
Reject detected QUIC only when the final outbound policy is a proxy. Traffic routed to DIRECT is allowed; traffic routed to REJECT remains rejected by that rule. |
For traffic entering through TUN, Chute replies to a rejected QUIC flow with an ICMP/ICMPv6 Port Unreachable message so compatible clients can fall back without waiting for a QUIC timeout.
QUIC detection for
block-quicis automatic and does not requiresniffing-enabled;sniffing-enabledcontrols TCP protocol sniffing. Use aPROTOCOL,QUIC,...rule when you need rule-based handling of individual QUIC flows.
Bypass TUN
bypass-tun = 192.168.0.0/16, 10.0.0.0/8
Similar to skip-proxy, but works at the TUN/VIF routing level. Connections to these IP ranges will bypass the TUN interface entirely and go through the system network stack directly.
Note: This option only works for the VIF (utun) type of Enhanced Mode. It has no effect when using the PacketTunnel VPN type.
Show Error Page for Reject
show-error-page-for-reject = true
When enabled, Chute returns a user-friendly error page for rejected requests instead of simply dropping the connection.
Optimistic DNS
optimistic-dns = false
When enabled, Chute returns the cached DNS result immediately while refreshing the record in the background. This reduces connection latency at the cost of possibly returning stale DNS records.
Default:
true. Setoptimistic-dns = falseto disable.
Allow Wi-Fi Access
allow-wifi-access = true
Lets other devices on the same network reach Chute's HTTP and SOCKS5 proxy ports, by widening both listeners to 0.0.0.0. Surge-style configurations — and the sing-box importer's allow-lan mapping — carry only this flag and leave interface at its 127.0.0.1 default, which is why the flag widens the bind address itself.
The flag and an explicit wildcard
interfaceare two spellings of the same intent, and Chute takes the union: a configuration that already writes0.0.0.0behaves exactly as before. Changing the flag rebinds both listeners, so withdrawing access takes effect on reload rather than at the next restart.These are the proxy ports, which have no authentication unless
http-authis set. On a network you do not control, set credentials as well.
Managed Configuration
#!MANAGED-CONFIG https://example.com/config.conf interval=86400 strict=false
A configuration whose first line is a #!MANAGED-CONFIG header is re-fetched from the URL automatically. The header's parameters and the update behavior are documented in Managed Configuration.
Replica / Traffic Recording
replica = true
The replica key in [General] (replica = true) enables traffic recording; the [Replica] section filters what the Chute Dashboard displays — its keys are documented in Replica.
External Controller Access (Chute Dashboard)
external-controller-access = password@0.0.0.0:6155
Starts the remote control server used by Chute Dashboard. The value is password@host:port; a username may optionally precede the password (user:password@host:port). On iOS, a companion USB channel is opened on port + 1 (not on macOS). Connecting it is described on the Chute Dashboard page.
HTTP Control API and Web UI
[General]
external-http-controller = 127.0.0.1:9090
external-http-secret = your-secret-token
external-http-ui = true
external-http-cors = false
Chute provides an embedded HTTP control API and web-based management UI, similar to Clash and Surge dashboards. The API exposes kernel status, traffic, connections, DNS, policy controls, and configuration management through REST endpoints.
external-http-controller (Default: disabled)
external-http-controller = 127.0.0.1:9090
The address and port for the HTTP control server. Use 127.0.0.1 for local-only access. Binding to any non-loopback address (for example 192.168.1.5:9090) requires an explicit external-http-secret — without one the server refuses to start and logs what is missing.
A wildcard address (0.0.0.0, ::, *) counts as non-loopback for this check, because it listens on every interface — the opposite of local-only. Accepted spellings are 0.0.0.0:9090, *:9090 (the same thing) and, for IPv6, [::]:9090 — a bare :::9090 is bracketed for you; an address that cannot be parsed is reported as a configuration error instead of silently leaving the controller off. The whole of 127.0.0.0/8 counts as loopback, not just 127.0.0.1.
external-http-secret (Default: a generated token)
external-http-secret = your-secret-token
The Bearer token used for API authentication. Requests must include the header Authorization: Bearer <secret> — the token is accepted only in this header, never as a query parameter, and is compared in constant time. Authentication failures return 401 with {"ok": false, "error": {"code": "unauthorized", "message": "missing or invalid token"}}. Only /api/* paths and the Clash-compatible aliases are guarded; the Web UI's static assets need no token (with external-http-ui = false they are gone altogether — the page is a 404).
When the key is absent, Chute generates a token instead of serving without authentication. The generated token is written to a control-token file in Chute's share folder — not beside the configuration: the app-group container on iOS and tvOS, the app's private data directory on Android — with owner-only permissions, is reused across restarts, and the apps show it to you — see the console entry points. The control API can hand out the connection database, whose records carry every URL, header and process name the run has seen, so an open controller is not a reasonable default.
To serve without authentication anyway, say so in the configuration:
external-http-secret = none
That is accepted only on a loopback bind. Off loopback, neither none nor an absent key is enough: the controller refuses to start and says what it wants.
Upgrading: if you relied on an absent
external-http-secretmeaning "no authentication", local scripts start receiving401. Either read the generated token out of the app, or writeexternal-http-secret = noneto keep the old behaviour deliberately.
external-http-ui (Default: true)
external-http-ui = true
When enabled, Chute serves an embedded Web UI at the controller address. The Web UI provides an overview dashboard, connection management, DNS inspection, traffic monitoring, policy controls, and configuration editing. Set to false to keep the API enabled while disabling the UI.
Opening the web console
The console is served at the controller's address, and each app can hand you the address and the token — including the generated one, which you would otherwise have no way to read:
- Chute Mac: menu bar → Open Web Console, and Copy Web Console Token. A connection's detail window also has Requests & History, which opens the console — its home page, not that connection.
- Chute iOS: Control Panel → the Web UI row → Open, Copy Address, or Copy Access Token.
- Chute tvOS: the control panel has a separate Web UI Address row below the Web UI switch, showing host and port. Selecting it shows a QR code when the listener is reachable from other devices — the sign-in token travels inside the code, not on the screen — and, when the controller is bound to loopback, an explanation instead, because a loopback console cannot be opened from a phone.
- Chute Android: the control panel shows the address as the HTTP API switch's subtitle, and a Generated access token row that copies on tap — that row appears only when the kernel generated the token; a secret you configured yourself is never displayed.
The address these produce carries the token as a query parameter. The page consumes it on load and removes it from the address bar, so opening the link is a complete sign-in and nobody has to transcribe 32 hex characters.
external-http-cors (Default: false)
external-http-cors = true
Enable CORS (Cross-Origin Resource Sharing) headers on API responses. Useful when the Web UI or third-party tools need to access the API from a different origin.
API Endpoints:
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/status |
Runtime status, ports, uptime |
GET |
/api/traffic |
Global and per-policy traffic counters |
GET |
/api/connections |
Current active connections |
DELETE |
/api/connections/:id |
Close a connection |
GET |
/api/connections/history |
Historical connection records |
GET |
/api/connections/processes |
Per-process connection statistics |
GET |
/api/connections/:id/request |
Captured request data for a connection |
GET |
/api/connections/:id/response |
Captured response data for a connection |
GET |
/api/dns |
DNS cache records |
DELETE |
/api/dns/cache |
Purge DNS cache |
DELETE |
/api/dns/records/:domain |
Remove a single DNS record |
GET |
/api/config |
Current configuration |
PUT |
/api/config |
Reload configuration |
POST |
/api/config/validate |
Parse a configuration and report its errors, without loading it |
GET |
/api/policies |
Policy groups and current selection |
PUT |
/api/policies/:group |
Change policy group selection |
PUT |
/api/mode |
Set outbound mode |
GET |
/api/features |
Feature switch states |
PUT |
/api/features/mitm |
Toggle MitM |
PUT |
/api/features/record-traffic |
Toggle traffic recording |
GET |
/api/rules |
Loaded rules, and which rewrite rules have fired |
POST |
/api/rules/match |
Where a request would be routed, without making it |
GET |
/api/logs |
Recent log entries |
GET |
/api/loglevel |
Current log level, and the sections being written |
PUT |
/api/loglevel |
Change the log level or sections without restarting |
GET |
/api/health |
Engine health: refusals, generations, footprint, previous exit |
GET |
/api/events |
Notable moments of this run |
GET |
/api/tailscale |
Live Tailscale engine state |
POST |
/api/diagnostics/ping |
ICMP or TCP reachability probe |
POST |
/api/diagnostics/dns-query |
Resolve a domain through the running resolver |
POST |
/api/diagnostics/egress-probe |
Detect the current egress IP |
POST |
/api/diagnostics/url-test/:policy |
Run a policy's latency test |
POST |
/api/diagnostics/bundle |
Build a redacted diagnostic archive |
GET |
/api/connections/export |
Export connections as HAR 1.2 |
GET |
/api/rewrites |
Every rewrite/mock family, plus the MitM host list |
GET |
/api/rewrites/:family |
One family's rules |
POST |
/api/rewrites/:family |
Add a rule |
DELETE |
/api/rewrites/:family/:id |
Remove a rule |
DELETE |
/api/rewrites/:family |
Clear a family |
GET |
/api/mitm/hosts |
Hosts currently decrypted |
POST |
/api/mitm/hosts |
Add a host |
DELETE |
/api/mitm/hosts/:host |
Remove a host |
DELETE |
/api/mitm/hosts |
Clear the list |
Endpoint notes:
GET /api/connectionsacceptslimit(positive integer, default and maximum 1000) andcursor(returns only connections withidgreater than the cursor). The responsedatacarriesconnections,total,page_size,has_more, and — when more pages exist —next_cursor.GET /api/connections/historyacceptslimit(default 100, maximum 1000) andcursor/before(synonyms; passing both is rejected).GET /api/connections/:id/requestand.../responsereturn{"connection_id": <id>, "data": "<base64>"}. Captures larger than 2 MiB return413.GET /api/configreturns the configuration with sensitive values (http-auth,external-http-secret,ca-p12,ca-passphrase, WireGuard keys, and similar) replaced by<redacted>— do not feed the result straight back intoPUT /api/config, or the placeholders are written into the configuration literally.PUT /api/configaccepts either JSON{"configuration": "<full text>"}or the raw configuration text as the request body (1 MB limit). On success the kernel reloads — or starts, if it was not running.PUT /api/policies/:grouptakes the selection from the first of the body keyspolicy,name,selected,select; the value may be a policy name or a numeric index as a string. The special group nameGLOBALsets the global selected policy.PUT /api/moderequires a JSON number:{"mode": 0}—0rule,1direct,2proxy.PUT /api/features/mitmandPUT /api/features/record-traffictake{"enabled": true}.GET /api/logsacceptssince(Unix seconds); the in-memory buffer keeps the most recent 1000 entries, each{timestamp, level, section, message}.GET /api/healthreports what the engine is holding and why it last refused something, plus how the previous run ended (clean,unclean, orsuspected_memory— see Troubleshooting).GET /api/eventsserves the in-memory ring;?persisted=1reads this run's stored history instead, and answersavailable: falsewhen record keeping is off.GET /api/tailscaleanswersstate: "idle"when no[Tailscale]section is configured — that is a normal answer, not an error.- The
POST /api/diagnostics/*probes take their arguments from the query string or a JSON body, are bounded to ten seconds, and answer exactly once.url-test/:policyrejects a policy name that is not defined rather than measuringREJECT. POST /api/rules/matchanswers where a request would go, without opening a connection. It takeshost(or aurlto take the host and port from),port(default 443), and optionalip,protocol,process,process_path,src_ip,src_port,in_port,in_type,in_user,in_name,network,ssid,bssid,from_tun,user_agent— an unknown field is refused with the accepted list rather than ignored. The reply carries thematchedrule (its line, type, the policy it names and theresolved_policya group currently points at), thepolicy,need_resolve, and therule_countandmatch_generationthe answer was computed against. Because a domain is matched twice — once on the name and again once an address is known —passesholds one entry per pass; without aniponly the pre-resolution pass exists and the reply says so innote. Addexplain=truefor the candidate rules that could also have matched, up to 50, with a count of the rule sets that were not expanded.POST /api/config/validateparses a configuration and throws it away: the running kernel adopts nothing. Send it as{"configuration": "<full text>"}or as the raw text. The reply isvalid,error_count,advisory_count,rule_count,policy_countand anerrorslist of{line, severity, content, error}—severityseparates a line that was refused from one accepted with a caveat, andcontentis the offending line redacted, since a bad[Proxy]line usually carries the password that made it bad. Use it beforePUT /api/config, which restarts the run you are debugging.GET /api/loglevelreports the currentlevel, thenslog_level, thesectionsbeing written to file, and theavailable_levels/available_sectionsyou may set.PUT(orPATCH) takeslevel,sections, or both:sectionsis an array of section names or the string"all", and an empty array is refused — to stop logging uselevelnone. Changing the level this way does not restart the run, which is the point:loglevel = verbosein the file needs a reload, and a reload loses the thing you were trying to see.GET /api/rulesalso reportsrewrite_hits— every rewrite or mock rule that has fired this run, with a count. A rule that does not appear there has never matched, which is the usual explanation for a rewrite that seems to do nothing. The table tracks up to 512 distinct rules and reportsrewrite_hit_dropped_rulesfor any beyond that.POST /api/rewrites/:familytakes{"rule": "<configuration line>"}— the same text you would write in the file. A line that does not parse is refused with400rather than stored as a rule that can never match.:familyis one ofurl-rewrite,header-rewrite,body-rewrite,mock. Rules added this way live in the running kernel and are not written back to the configuration file.GET /api/connections/export?format=harreturns a HAR 1.2 document, andPOST /api/diagnostics/bundlereturns a zip — both are files, so unlike every other endpoint they are not wrapped in the{"ok": ..., "data": ...}envelope.exportacceptssource(current, the default, orhistory),limit(default 100, maximum 300),ids, andbodies=1to include the captured payloads. Each entry carries a_klobject with what HAR has no field for: the chosen policy, the matched rule, and the rewrites that fired.
Example — read the status, then switch a policy group:
curl -H "Authorization: Bearer your-secret-token" http://127.0.0.1:9090/api/status
{"ok":true,"data":{"running":true,"outbound_mode":0,"mitm":false, ...}}
curl -X PUT -H "Authorization: Bearer your-secret-token" \
-d '{"policy": "ProxyB"}' http://127.0.0.1:9090/api/policies/MainGroup
{"ok":true,"data":{"outbound_mode":0,"selectable_groups":[...], ...}}
Example — ask where a request would go, before making it:
curl -X POST -H "Authorization: Bearer your-secret-token" \
-d '{"url": "https://api.example.com/v1/orders", "explain": true}' \
http://127.0.0.1:9090/api/rules/match
{"ok":true,"data":{"policy":"MainGroup","matched":{"rule":"DOMAIN-SUFFIX,example.com,MainGroup", ...}, ...}}
Narrow the log to one subsystem while reproducing, without restarting the run:
curl -X PUT -H "Authorization: Bearer your-secret-token" \
-d '{"level": "verbose", "sections": ["MitM", "DNS"]}' \
http://127.0.0.1:9090/api/loglevel
Note: This feature is disabled by default. Every response uses the envelope
{"ok": true, "data": {...}}on success and{"ok": false, "error": {"code": "...", "message": "..."}}on errors; request bodies are limited to 1 MB.
PUTendpoints also acceptPATCH. Clash-compatible alias paths are available for third-party dashboards:/version,/traffic,/connections,/configs,/proxies,/rules— the paths are reachable, but responses use Chute's envelope and field names rather than Clash's schema (/versionreturns only{"name", "run_id"}), so Clash dashboards will not work out of the box.
Client Fingerprint
global-client-fingerprint = chrome
Sets the TLS client fingerprint for every policy that does not carry its own fingerprint. A policy's own value always wins, so this is a default rather than an override.
Supported values are chrome, firefox, safari and ios, plus edge, 360,
qq, android and random, which are all handled as Chrome. An unrecognised
value is ignored and the platform TLS stack is used.
Default: empty, meaning the platform TLS stack. Only Trojan, VMess, VLESS and ShadowTLS policies read it.