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 and tvOS version, a hostname entry is added to the tunnel's proxy exception list, so connections to it are handled by Chute TUN instead of Chute proxy; an IP address or CIDR entry additionally becomes an excluded route of the tunnel, so traffic to that range leaves the tunnel entirely rather than being handled by Chute TUN. Chute Android does the same in VPN mode: while System HTTP Proxy is on, hostnames, * wildcards and IPv4 addresses join the exclusion list of the proxy the VPN hands to apps, and on Android 13 and later every IP address or CIDR entry becomes an excluded route of the VPN — a bare address as a /32 or /128 route; loopback entries are left out, since loopback traffic never enters the tunnel. 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).

The HTTP proxy server carries one exchange per connection for a plain http:// request: each response goes out with Connection: close and the connection closes after it, so the client sends its next request on a new connection. A request pipelined behind the first is dropped, and the client sends it again on the new connection. An authentication scheme that needs several round trips on the same connection, such as NTLM or Negotiate, cannot complete through it. A CONNECT tunnel that is not decrypted is not affected. A WebSocket upgrade keeps its connection: after the 101 response, data passes unchanged both ways.

Compatibility aliases: doh-server and doh-service are accepted as aliases of doh; 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 sets allow-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.

The HTTP proxy answers a request without valid credentials — a plain request or a CONNECT — with 407 Proxy Authentication Required and Proxy-Authenticate: Basic realm="KLNEKit", then closes the connection. The SOCKS5 server takes the same credentials through SOCKS5 username/password authentication and closes a connection that does not offer it or fails it.


Bypass System Requests

bypass-system = true

When enabled, Chute appends a fixed list of 18 Apple hostnames — push, iCloud gateway, captive-portal check, OCSP and similar — to skip-proxy, and appends IP-CIDR,17.0.0.0/8,DIRECT,no-resolve after your rules, just before FINAL — so a rule of yours that matches 17.0.0.0/8 first still wins. There is no process-based matching: the option is that fixed host and address list, not a filter on which process made the request.

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. always-ip-address (Shadowrocket's spelling) is read as this key: a boolean value (true, yes, on, 1) means every host and is rewritten to always-real-ip = *; false is dropped; any other value is read as a host list. The configuration is saved with the always-real-ip spelling.


Read the System Hosts File

read-etc-hosts = false

Whether the system hosts file is read into the [Host] table. Set it to false to ignore that file; a change takes effect on reload. See Local DNS Mapping.

Default: true.


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.

This applies to every group a connection passes: the group its rule names, a group nested in it, the upstream group of a chained policy (underlying-proxy), and a relay group's member groups.

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 / iOS / 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: true on macOS, false on 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. This is the macOS system proxy's own "Exclude simple hostnames" setting: Chute Mac writes it into the system proxy configuration, so it applies to applications that follow the system proxy and to nothing else — not to Enhanced Mode or TUN traffic. On iOS and tvOS it is written into the tunnel's proxy settings in the same way, so a simple hostname is handled by Chute TUN instead of Chute proxy, as a hostname in skip-proxy is. Chute Android does not use it.

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-show-speed = true

When enabled, Chute Mac shows the current upload and download speed in the menu bar.

Parsed but not effective in the current engine — nothing in Chute Mac reads the key. The menu bar speed is toggled from the menu item Show Connection Speed, which is stored in the app's own settings. The key is still written back when the configuration is saved.


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.

An entry without a port means port 53, so hijack-dns = 8.8.8.8 is 8.8.8.8:53. An entry that cannot be read is skipped with a notice; the rest of the line still applies.

Fake IP is available on Apple TV too (tvOS 17 and later). It had been disabled there; verification on a device is still in progress.


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 applies to the iOS and tvOS packet tunnels, where the ranges become excluded routes of the tunnel's IP settings, and to Chute Android on Android 13 and later, where they become excluded routes of the VPN — Android 11 and 12 have no API for excluded routes, so the ranges are ignored there. It has no effect on macOS, in either Enhanced Mode type — neither the helper's utun nor the Mac Network Extension excludes any route. 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.

The reason a default route is not enough: the system picks a route by prefix length, not by order. The physical interface's own on-link subnet — 192.168.1.0/24, say — is more specific than the tunnel's 0.0.0.0/0, so that traffic never reaches Chute. An entry here installs a still more specific route and wins. The routes are added on top of the default one and never narrow what the tunnel captures.

Note: Avoid listing the private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) wholesale. It is normally unnecessary and can upset the system's own routing; Chute honours such an entry but logs a warning — except under the Network Extension flavour of Enhanced Mode on macOS, which honours it without one. The usual reason to use this option is reaching a range another VPN app's tunnel owns.

Note: An entry that would cut the branch Chute sits on is refused with the reason in the log: loopback, the VIF's own subnets (198.18.0.0/15 and fd12:1:1:1::/64), link-local, multicast, broadcast, and a prefix length of 0. The helper-utun flavour of Enhanced Mode on macOS also refuses an entry that covers the current default gateway. Under the Network Extension flavour the reason goes to the system log rather than to Chute's.

Note: On iPhone the system decides local-network capture separately. A local subnet listed here is honoured only when Include Local Networks is also on in the app's settings — a switch that can be turned on only while Include All Networks is on; a range owned by another app's tunnel needs no such switch. Chute tvOS shows the same switches but does not apply them to the tunnel. On macOS the routes are installed directly and no such gate applies.

Note: On macOS the helper-utun flavour of Enhanced Mode installs these routes when the interface is attached, so a changed list re-attaches the interface on reload. This option needs helper version 0.8.6 or later — after updating Chute, approve the helper reinstall prompt once, or the helper-utun flavour reports that the helper is not running.


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. In Enhanced Mode the same switch lets an HTTP/3 (QUIC) flow to a bare IP address be matched by name: Chute reads the server name from the QUIC ClientHello, DOMAIN-type rules see that name instead of the address, and every later packet of the flow follows the verdict of the first. A ClientHello too large for one packet — common with post-quantum key exchange — is reassembled per flow from the Initial packets that carry it, up to a reassembled hello of 16 KiB, so its name is read as well. While the rest of the hello is still coming, the flow's datagrams are held rather than sent by their bare address, and they go out together behind the name once it is read — so the first fragment of a split hello no longer takes a different route from the rest of it. The backstops are a one-second wait, eight datagrams or 16 KiB held per flow, and 64 flows assembling at once; a flow past any of these, or one whose Initial packets never complete the ClientHello, is matched by address as before.

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.
all Surge's spelling of on.
all-proxy, per-policy Surge's spellings, both read as auto — Chute has no per-policy QUIC blocking, so per-policy blocks QUIC on every proxied flow. The word you wrote is kept when the profile is saved.

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.

A rejected QUIC flow is recorded with REJECT as its policy, whichever policy the rule chose.

QUIC detection for block-quic is automatic and does not require sniffing-enabled; sniffing-enabled controls protocol sniffing — TLS on TCP, and the server name of a QUIC flow. Use a PROTOCOL,QUIC,... rule when you need rule-based handling of individual QUIC flows.


Answer ICMP in the Tunnel

icmp-auto-reply = false

An ICMP echo request that enters the tunnel — IPv4 or IPv6 — is answered by Chute itself: the reply is synthesised locally and nothing is sent to the target. Every destination therefore answers, an address a rule would REJECT and a host that is down included, so a ping that succeeds inside the tunnel says nothing about whether the target is reachable. Set this to false to stop answering: the request is then relayed out of the device, but its reply never comes back through the tunnel, so the ping is never answered.

Default: true. Applies to traffic arriving through TUN; ICMP is never proxied.


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 applies to the iOS and tvOS packet tunnels, where its ranges join tun-excluded-routes as excluded routes of the tunnel, and in the same way to Chute Android on Android 13 and later (Android 11 and 12 ignore it). It has no effect on macOS, in either Enhanced Mode type.


Show Error Page for Reject

show-error-page-for-reject = true

When enabled, Chute returns a user-friendly error page for rejected requests. This applies only to a request that arrived through the HTTP proxy inbound; with the option off, such a request is answered with HTTP/1.1 503 Service Unavailable instead. A rejected request from any other inbound is dropped either way.

Default: false.


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. With it off, a record whose TTL has passed is dropped instead of served and the query goes upstream; the refresh still happens, so the next lookup is answered from cache. It also governs, on every platform, whether the tunnel dials several of a name's addresses at once.

Default: true. Set optimistic-dns = false to 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 interface are two spellings of the same intent, and Chute takes the union: a configuration that already writes 0.0.0.0 behaves 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-auth is 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.

On iOS, tvOS and Android an active licence is required as well: without it replica = true captures nothing.


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 and tvOS, 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 Console

[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 Console, similar to Clash and Surge dashboards. The API exposes engine status, traffic, connections, DNS, policy controls, and configuration management through REST endpoints.

Surge's http-api = <secret>@<host>:<port> is read as external-http-controller plus external-http-secret. A profile may also spell the parts separately: http-api-secret, http-api-ui and http-api-cors are read as external-http-secret, external-http-ui and external-http-cors, and http-api-web-dashboard also as external-http-ui; the configuration is saved with the external-http-* keys. http-api-tls is not supported — the controller speaks plain HTTP — and only produces a notice.

external-http-controller (Default: disabled)

external-http-controller = 127.0.0.1:9090

The requests the console keeps resident while serving are capped at 4 MB on macOS and 1 MB on iOS, tvOS and Android.

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"}}. Every data route is guarded: the /api/* paths, the Clash-compatible aliases, and the older routes that only Chute Android still serves (/status, /policies, /dns, /records and the like), which accept the token only in this header as well. Chute Android's own Remote Dashboard now uses the /api/* paths only. The Web Console's page and static assets (/, /assets/…, and on Chute Android also /ui) 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-secret meaning "no authentication", local scripts start receiving 401. Either read the generated token out of the app, or write external-http-secret = none to keep the old behaviour deliberately.

external-http-ui (Default: true)

external-http-ui = true

When enabled, Chute serves an embedded Web Console at the controller address. The Web Console provides an overview, 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 Console row → Open, Copy Address, or Copy Access Token.
  • Chute tvOS: the control panel has a separate Web Console Address row, 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: Control Panel → Web Console Address → Open or Copy Address, and a Generated access token row that copies on tap. Neither row appears when the token is a secret you configured yourself, which 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 Console 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; every entry carries hidden, which is true for a group marked hidden=true
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 The rule table the matcher walks, where each rule came from, 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
GET /api/scripts The generic scripts that can be run on demand
POST /api/scripts/run Run one generic script by name
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 Check the address: this device's own on the Apple engines, the public egress address on Android
POST /api/diagnostics/internet-test The direct Internet test: fetch internet-test-url without a proxy
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 Response 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
GET /api/rules/providers Rule providers and rule sets, with the status of each — including why one did not load

Endpoint notes:

  • GET /api/connections accepts limit (positive integer, default and maximum 1000) and cursor (returns only connections with id greater than the cursor). The response data carries connections, total, page_size, has_more, and — when more pages exist — next_cursor.
  • GET /api/connections/history accepts limit (default 100, maximum 1000) and cursor/before (synonyms; passing both is rejected).
  • GET /api/connections/:id/request and .../response return {"connection_id": <id>, "data": "<base64>"}. Captures larger than 2 MiB return 413.
  • GET /api/config returns 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 into PUT /api/config, or the placeholders are written into the configuration literally.
  • GET and PUT /api/config are also reachable as /api/configs, and as the Clash alias /configs.
  • PUT /api/config accepts either JSON {"configuration": "<full text>"} or the raw configuration text as the request body (1 MB limit). On success the engine reloads — or starts, if it was not running.
  • PUT /api/policies/:group takes the selection from the first of the body keys policy, name, selected, select; the value may be a policy name or a numeric index as a string. The special group name GLOBAL sets the global selected policy.
  • PUT /api/mode requires a JSON number: {"mode": 0} — 0 rule, 1 direct, 2 proxy.
  • PUT /api/features/mitm and PUT /api/features/record-traffic take {"enabled": true}.
  • GET /api/logs accepts since (Unix seconds); the in-memory buffer keeps the most recent 1000 entries, each {timestamp, wall_time, level, section, message} — timestamp and wall_time carry the same Unix-seconds value, nudged forward when the clock does not advance so entries stay strictly ordered.
  • GET /api/health reports what the engine is holding and why it last refused something, plus how the previous run ended (clean, unclean, or suspected_memory — see Troubleshooting).
  • GET /api/events serves the in-memory ring; ?persisted=1 reads this run's stored history instead, and answers available: false when record keeping is off.
  • GET /api/tailscale answers state: "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/:policy rejects a policy name that is not defined rather than measuring REJECT.
  • GET /api/scripts lists the enabled generic scripts as {name, type}; POST /api/scripts/run takes name from the body or the query string, is bounded like the diagnostics probes, and answers {name, timedOut, result} — timedOut tells a script that never called $done apart from one that ran to completion. A name that is not an enabled generic script is a 404.
  • POST /api/rules/match answers where a request would go, without opening a connection. It takes host (or a url to take the host and port from; a URL-REGEX rule is tested against that url exactly as written, although in real traffic it only ever sees plain http:// requests), port (default 443), and optional ip, 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 the matched rule (its line, type, the policy it names and the resolved_policy a group currently points at), the policy, need_resolve, and the rule_count and match_generation the answer was computed against. Because a domain is matched twice — once on the name and again once an address is known — passes holds one entry per pass; without an ip only the pre-resolution pass exists and the reply says so in note. Add explain=true for 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/validate parses a configuration and throws it away: the running engine adopts nothing. Send it as {"configuration": "<full text>"} or as the raw text. The reply is valid, error_count, advisory_count, rule_count, policy_count and an errors list of {line, severity, content, error} — severity separates a line that was refused from one accepted with a caveat, and content is the offending line redacted, since a bad [Proxy] line usually carries the password that made it bad. Use it before PUT /api/config, which restarts the run you are debugging.
  • GET /api/loglevel reports the current level, the nslog_level, the sections being written to file, and the available_levels / available_sections you may set. PUT (or PATCH) takes level, sections, or both: sections is an array of section names or the string "all", and an empty array is refused — to stop logging use level none. Changing the level this way does not restart the run, which is the point: loglevel = verbose in the file needs a reload, and a reload loses the thing you were trying to see.
  • GET /api/rules also reports rewrite_hits — every rewrite or Mock Response 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 reports rewrite_hit_dropped_rules for any beyond that. rules lists every rule the matcher walks, in order: the automatic Tailscale rules, then module rules, then the configuration's [Rule] section, ending with FINAL; rule_regions names each line's origin, index for index (front, module, configuration).
  • POST /api/rewrites/:family takes {"rule": "<configuration line>"} — the same text you would write in the file. A line that does not parse is refused with 400 rather than stored as a rule that can never match. :family is one of url-rewrite, header-rewrite, body-rewrite, mock. Rules added this way live in the running engine and are not written back to the configuration file.
  • GET /api/connections/export?format=har returns a HAR 1.2 document, and POST /api/diagnostics/bundle returns a zip — both are files, so unlike every other endpoint they are not wrapped in the {"ok": ..., "data": ...} envelope. export accepts source (current, the default, or history), limit (default 100, maximum 300), ids, and bodies=1 to include the captured payloads. Each entry carries a _kl object with what HAR has no field for: the chosen policy, the matched rule, and the rewrites that fired.
  • A chained connection's record in GET /api/connections and GET /api/connections/history carries chainPath, the path from this device to the exit, as in Airport/HK-01 → Landing; it is empty for a connection that used a single policy, and the HAR export carries it as _kl.chain. In the per-policy counters of GET /api/traffic, an upstream also counts the bytes it carried for chained connections; the global totals count them once.

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.

PUT endpoints also accept PATCH. 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 (/version returns {"name", "run_id", "egress_probe"}, where egress_probe is network_address on the Apple engines and egress_ip on Android), so Clash dashboards will not work out of the box.


Proxy Test URL

[General]
proxy-test-url = http://cp.cloudflare.com/generate_204
test-timeout = 3

proxy-test-url is the probe URL of every url-test, fallback and load-balance group that does not set its own url, and test-timeout (seconds) the timeout of every such group without a timeout. Groups keep following these keys after the configuration is saved; only a url or timeout written on the group line overrides them. An invalid proxy-test-url is a configuration error. internet-test-url is used by the Web Console's direct Internet test, fetched without a proxy; when omitted, the engine uses its built-in success URL.


UDP Through a Policy Without UDP

udp-policy-not-supported-behaviour = DIRECT

What happens to a UDP datagram whose policy cannot relay UDP, such as a plain HTTP proxy: REJECT (the default) drops it; DIRECT sends it directly instead. block-quic is decided first, on the policy the rule chose: with block-quic = auto, QUIC headed for a proxy that cannot relay UDP is rejected rather than sent directly. Other UDP still falls back to DIRECT. UDP of a chained policy that cannot go through its upstream is handled the same way, and so is a DoQ or DoH3 upstream that follows the outbound mode into a policy without UDP: REJECT skips that upstream, DIRECT asks it directly.


Front Proxy

[General]
global-underlying-proxy = Airport

Sends every proxy policy that has no underlying-proxy of its own through the named policy or group — what Shadowrocket calls a front proxy (前置代理), which it sets only in its app. The policies of proxy providers are included. Left as they are: every policy a connection through the front policy itself can pass — its members, their upstreams and the hops of a relay among them — so the front policy never runs through itself; a policy that writes underlying-proxy=DIRECT, which opts out; policy groups, whose members decide; and DIRECT, REJECT and TAILSCALE. Leave the key out, or write DIRECT, to turn it off. A name that is not defined refuses every policy it would cover rather than letting it connect directly.

close-if-proxy-chain-missing (Shadowrocket) is read and written back when the profile is saved. Chute always does what its true does: a chained policy whose upstream is missing is refused. false — Shadowrocket's default, which skips the missing hop and connects to the node directly — is not followed, and a notice says so once.


Client Fingerprint

global-client-fingerprint = chrome

Sets the TLS client fingerprint for every policy that does not carry its own fingerprint, the policies a proxy provider supplies included. 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, and the other names listed under fingerprint. An unrecognised value is ignored with the warning Ignoring unsupported global-client-fingerprint '<value>', and the platform TLS stack is used.

Default: empty, meaning the platform TLS stack. It always reaches ShadowTLS policies, and reaches VLESS ones that set tls=true or reality=true, Trojan and VMess ones that set tls=true, and Shadowsocks ones that set both ws=true and tls=true; a policy on the gRPC transport needs tls=true written as well. ShadowsocksR policies never read it.

S. Smart Rabbit LLC © All Rights Reserved            updated 2026-09-29 21:57:05

results matching ""

    No results matching ""