Body Rewrite
Chute can search and replace content in HTTP request and response bodies using regular expressions or JSONPath expressions. This requires MitM decryption for HTTPS traffic.
Notice: A plain HTTP request is processed only when it reaches Chute through its HTTP proxy; plain HTTP that arrives through the TUN interface is forwarded untouched. Chute Android sends all traffic through TUN unless System HTTP Proxy is turned on in Settings, which hands apps Chute's HTTP proxy (Android 10 and later; off by default).
Surge's http-request-jq and http-response-jq programs are supported as well — see Surge Syntax.
Body rewrite rules are defined in the [Body Rewrite] section. For each direction (request / response), only the first matching rule is applied to a message. When an http-request or http-response script that takes the body matches the same message, the rule is applied first and the script sees the rewritten body; an http-request-before-send script runs after both.
[Body Rewrite]
^https://api\.example\.com/response.* response regex "old-text" "new-text"
^https://api\.example\.com/request.* request regex "sensitive" "[redacted]"
^https://api\.example\.com/data.* jsonpath-response jsonpath $.ads null
Rule Format
Each rule follows this general format:
<URL regex> [direction] <mode> <pattern> <replacement>
Notice: The URL regular expression is matched anywhere in the request's full URL, and in its path alone, as in URL Rewrite:
^https://example\.comalready covers every path and query string under that host, so a trailing.*is unnecessary, and^/apimatches every request whose path starts with/api. The full URL of a decrypted request starts withhttps://, so a^http://pattern never matches one. The pattern keeps its case, so\S,\D,\Wand\Bmean what they say; the match itself ignores case.
A # or // at the start of the line or after a space begins a comment that runs to the end of the line, unless it is inside double quotes; ; never does. See Comment.
Surge Syntax
Surge's own line shape is accepted as well: the direction comes first, and the regex keyword is left out.
[Body Rewrite]
http-response ^https://api\.example\.com/feed "\"ads\":\s*\[.*?\]" "\"ads\":[]"
http-request ^https://api\.example\.com/submit "sensitive" "[redacted]"
A Surge line may carry several pattern/replacement pairs; they are applied left to right, each to the result of the one before. http-request-jq and http-response-jq take a jq program instead of a pattern and a replacement: <type> <URL pattern> <jq program>, with the program usually quoted because it contains spaces. Outside the quotes, a // or # that follows a space starts a comment, which ends the program there; inside them // is jq's alternative operator, so quote a program that uses it. It runs over the body as JSON. A body that is not JSON, a program that raises, and a program that produces no output all leave the body exactly as it was; an invalid program is reported and that one rule is skipped, without making the rest of the profile fail. A program cannot read files or the environment: import and include resolve nothing, and $ENV and env are empty.
Notice: Chute Android runs jq programs on jackson-jq. Its output is capped at 4096 results or 4 MB — a program that produces more leaves the body as it was — and it lacks some jq 1.7 builtins, among them the date functions other than
now,todateiso8601andfromdateiso8601(strftime,strptime,mktime,gmtime,todateand the like),@base32and@base32d,abs,toarray,trim,ltrimandrtrim,IN,INDEXandJOIN,tostreamandfromstream. A program that calls one of them raises when it runs, which leaves the body as it was.
Direction
| Keyword | Description |
|---|---|
response |
Apply to response body (default if omitted) |
request |
Apply to request body |
Modes
| Mode | Description |
|---|---|
regex |
Regular expression search-and-replace |
jsonpath-response / jsonpath-request / body-jsonpath-response / body-jsonpath-request |
JSONPath-based modification |
Regex Mode
Performs standard regex find-and-replace on the decoded body text. Uses NSRegularExpression (ICU) with case-insensitive matching. The replacement supports capture group references ($1, $2, etc.).
<URL regex> [response|request] regex <pattern> <replacement>
Example — remove ads from JSON response:
[Body Rewrite]
^https://api\.example\.com/feed.* response regex "\"ads\":\s*\[.*?\]" "\"ads\":[]"
Example — sanitize request body:
[Body Rewrite]
^https://api\.example\.com/submit.* request regex "\"password\":\s*\".*?\"" "\"password\":\"[FILTERED]\""
Example — use capture groups to reformat data:
[Body Rewrite]
// Swap "last, first" to "first last"
^https://api\.example\.com/users.* response regex "\"name\":\s*\"(\w+),\s*(\w+)\"" "\"name\":\"$2 $1\""
Example — rewrite embedded URLs in response body:
[Body Rewrite]
^https://api\.example\.com.* response regex "https://old-cdn\.example\.com" "https://new-cdn.example.com"
Tokens containing spaces must be quoted with double quotes:
[Body Rewrite]
^https://example\.com.* response regex "old value with spaces" "new value"
To include a literal double quote inside a quoted token, escape it with backslash: \".
JSONPath Mode
Modifies JSON bodies using JSONPath expressions. Supports reading, setting, and deleting values at specific paths.
<URL regex> jsonpath-response|jsonpath-request jsonpath <jsonpath-expression> [value]
Supported JSONPath Syntax
| Expression | Description |
|---|---|
$.key |
Access object property |
$.key.subkey |
Access nested properties |
$[0] |
Access array element by index |
$.key[0].subkey |
Mixed object and array access |
$.items[*].name |
Wildcard: all items in array |
$.*.value |
Wildcard: all properties |
.* takes every member of an object and every element of an array, so $.items.*.name reaches the name of each item. As the last step on an array it changes nothing; to replace the elements themselves, use [*].
Value Types
| Value | Result |
|---|---|
string |
Set to string value — anything that is not one of the forms below, so example.com, 1.0.0-beta and 12abc stay strings |
42 |
Set to integer — a token that is a JSON number in full, with no fraction or exponent |
3.14 |
Set to float — a JSON number with a fraction or an exponent, such as 1e3, or an integer too large for 64 bits |
true |
Set to boolean true |
false |
Set to boolean false |
[…] or {…} |
Unquoted, parsed as JSON and set as that array or object; it is the last field, so the rest of the line is taken as written and may contain spaces. Quoted, or not valid JSON, it stays a string. |
null, nil or omitted |
Delete the path |
Notice: Quoting a value does not force string type — quotes are stripped during tokenization and the type is inferred from the remaining content, so
"42"becomes the number 42,"true"becomes boolean true and"null"deletes the path; the quotes matter only for[…]and{…}, which stay strings when quoted. The keywords are matched exactly, soTrueandNULLare strings. A number counts only when the whole token is one, written as JSON writes it:+1,.5,1.and007stay strings, and so does a number too large for a float, such as1e400. There is no spelling that sets a string such as"42"or"true"— use aregexrule for it instead.
Example — set a JSON field:
[Body Rewrite]
^https://api\.example\.com/profile.* jsonpath-response jsonpath $.user.name "Anonymous"
Example — delete a JSON field:
[Body Rewrite]
^https://api\.example\.com/data.* jsonpath-response jsonpath $.tracking null
Example — wildcard modification:
[Body Rewrite]
^https://api\.example\.com/list.* jsonpath-response jsonpath $.items[*].hidden true
Practical Examples
Strip Tracking Parameters from JSON Responses
Remove the trackingId field from all API responses. Since only the first matching rule per direction is applied, use one rule per URL pattern:
[Body Rewrite]
^https://api\.example\.com/.* jsonpath-response jsonpath $.trackingId null
Inject a script tag into HTML responses
Append a custom <script> tag before </body> in all HTML pages:
[Body Rewrite]
^https://www\.example\.com/.* response regex "</body>" "<script>console.log('injected')</script></body>"
Redact sensitive fields in request logs
Replace API keys and tokens in outgoing request bodies before they reach the server. Since only the first matching rule per direction is applied, combine both fields into a single rule:
[Body Rewrite]
^https://api\.example\.com/.* request regex "\"(apiKey|token)\":\s*\"[^\"]+\"" "\"$1\":\"[REDACTED]\""
Normalize date formats in responses
Replace ISO dates with a shorter format:
[Body Rewrite]
^https://api\.example\.com/.* response regex "(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z" "$1/$2/$3 $4:$5"
Disable feature flags in app config
Force all feature flags to false in a configuration endpoint:
[Body Rewrite]
^https://api\.example\.com/config.* jsonpath-response jsonpath $.features[*].enabled false
Rewrite CDN URLs in cached responses
Replace all references to an old CDN with a new one:
[Body Rewrite]
^https://www\.example\.com/.* response regex "https://old-cdn\.example\.com" "https://new-cdn.example.com"
Processing Pipeline
Body rewrite automatically handles:
- Content-Encoding: Supports
gzipanddeflate. Skips unsupported encodings. - Transfer-Encoding: De-chunks chunked transfer encoding before processing.
- Decoding: Decompresses bodies before applying rewrite rules.
- Re-encoding: Re-compresses bodies and updates
Content-Length. RemovesTransfer-Encodingheader. - Accept-Encoding: When a response rule matches a request's URL, the request's
Accept-Encodingheader is rewritten togzip, deflate, identityso that the response stays in a decodable encoding.
Max body size for rewrite processing is 128KB on HTTP/1.x (plain HTTP and decrypted HTTP/1.1); larger bodies are passed through without modification. A decrypted HTTP/2 message is buffered whole and rewritten whatever its size.
On HTTP/1.x a response body is rewritten once all of it has arrived. When the server closes the connection first, a body with neither
Content-Lengthnor chunked encoding is complete — the close is its end — and is rewritten as usual. AContent-Lengthor chunked body that the close cut short goes to the client exactly as it arrived, not rewritten and with itsContent-Lengthuntouched, and the connection is then closed, so the client can tell that the response is incomplete.
Notes
- For HTTPS traffic, MitM decryption must be enabled for the matching hostname.
- Regex matching is case-insensitive. The replacement template supports ICU capture group references:
$0(full match),$1(first group),$2(second group), etc. - A URL pattern or a body pattern that is not a valid regular expression makes a regex or JSONPath line a configuration error, and the rule is not loaded; a jq line is skipped with a warning instead. In the pairs after the first, an invalid pattern drops only that pair, with a warning in the log.
- JSONPath mode only applies if the body is valid JSON.
- Body rewrite rules are applied to the decoded (UTF-8) body text.
- Only the first matching rule per direction (request / response) is applied to a message. Define one combined rule if you need multiple edits.
- Deleting a non-existent JSONPath leaves the body unchanged. Setting a value creates the key when its parent object exists; if an intermediate path is missing, nothing happens.