Debugging tools
Rewrite
Since 0.2.0 Requires Cloak Proxy 0.2.0 or later.
Change headers, query parameters, the URL, bodies and the status code of matching requests and responses in Cloak Proxy, without writing a script.
Rewrite changes requests and responses as they pass through Cloak Proxy, using a list of actions instead of code. Each action sets, adds, removes or replaces one thing: a header, a query parameter, text in the URL or a body, or the response status. Use it for the edits you’d otherwise write a short script for, such as adding an Authorization header, stripping a tracking parameter, turning on a feature flag in a response, or making an endpoint return 503.
Add a rewrite rule
Section titled “Add a rewrite rule”-
Right-click a request in the traffic list and choose Tools › Rewrite….
Cloak Proxy creates a rule that matches the request’s method and URL (without its query string), with subpaths off, turns Rewrite on and opens the rule. To start from scratch instead, choose Tools › Edit Rewrite Rules… (⌥ ⌘ R) and click +. A new rule is named New Rewrite and matches nothing until you fill in its URL.
-
Check the matcher. To cover a whole API, use a pattern such as
api.example.com/v1/*. See Rules and matching. -
Under Actions, click Add Action and choose what to change: Request header, Query parameter, URL, Request body, Response header, Response body or Response status.
-
Pick an operation in the second menu and fill in the fields. Add as many actions as you need.
-
Make the request again in your app. When a rule changed it, the traffic list shows a
Rewritebadge before the path.
Changes apply right away; there’s no Save button. The rule list shows each rule’s name and the method and URL it matches.
One rule, four actions. They run top to bottom on every request the rule matches.
- Only these apps: leave empty to match every app.
- Turns this one action on or off.
- What to change.
- How to change it. The choices depend on the target.
- Regex: Find is a regular expression; use $1, $2 in the replacement.
- Add Action: pick a target to add another action.
Targets and operations
Section titled “Targets and operations”Each action has a target (what it changes) and an operation (how). The operation menu only offers the operations that make sense for the target. A new action starts with the first operation in its list.
| Target | Operations | Changes |
|---|---|---|
| Request header | Set, Add, Remove, Find and replace | A header the server receives. |
| Query parameter | Set, Remove, Find and replace | A parameter in the request URL’s query string. |
| URL | Find and replace | The full request URL: scheme, host, port, path and query. |
| Request body | Find and replace | Text in the body the server receives. |
| Response header | Set, Add, Remove, Find and replace | A header the app receives. |
| Response body | Find and replace | Text in the body the app receives. |
| Response status | Set | The status code the app receives. |
What each operation does
Section titled “What each operation does”| Operation | Fields | On a header | On a query parameter | On the URL or a body | On the status |
|---|---|---|---|---|---|
| Set | A name and a value (for the status, only the code) | Sets the header to the value. If the header appears more than once, one copy is kept, in the first one’s position. If it’s missing, it’s added at the end. | Removes every parameter with that name and adds one with the value at the end. | Sets the status code, for example 503. |
|
| Add | A name and a value | Adds another header with that name, keeping any that are already there. | |||
| Remove | A name | Removes every header with that name. | Removes every parameter with that name. | ||
| Find and replace | A name (headers and parameters only; empty means all), Find, Replace with and Regex | Replaces text in the value of the named header, or of every header when the name is empty. | Replaces text in the value of the named parameter, or of every parameter when the name is empty. | Replaces every occurrence in the URL or body. |
The name field’s placeholder says Header or Parameter, or Name (empty: all) for Find and replace. The value field says Value, or Status code, e.g. 503 for the status. How names and values are read:
- Header names match regardless of case, so
authorizationandAuthorizationare the same header. - Query parameter names match exactly, including case.
- Values are sent exactly as you type them. Cloak Proxy doesn’t fill in variables, so
{{token}}is sent as the literal text{{token}}. - Find is case-sensitive. An empty Find leaves the text unchanged.
- Response status accepts a number from 100 to 599. Anything else is ignored and the server’s status is kept.
- Set, Add and Remove on a header or parameter do nothing while the name is empty.
Turn actions on and off
Section titled “Turn actions on and off”The checkbox at the start of each action turns it on or off. A turned-off action is dimmed and skipped. The minus button at the end of the row removes the action. To pause the whole rule, clear its checkbox in the rule list; to pause every rule, use the tool’s On/Off switch.
Replace text with a regular expression
Section titled “Replace text with a regular expression”Turn on Regex to treat Find as a regular expression. Every match is replaced, and Replace with can use $1, $2 and so on for the parts of the match in parentheses ($0 is the whole match).
| Find | Replace with | Before | After |
|---|---|---|---|
MyApp/([0-9.]+) |
MyApp/$1-debug |
MyApp/4.2.0 (iPhone) |
MyApp/4.2.0-debug (iPhone) |
"email":\s*"[^"]*" |
"email": "ada@example.com" |
"email": "someone@example.org" |
"email": "ada@example.com" |
/v1/([a-z]+)/ |
/v2/$1/ |
https://api.example.com/v1/users/42 |
https://api.example.com/v2/users/42 |
Matching is case-sensitive unless you start the pattern with (?i). A pattern that isn’t a valid regular expression leaves the text unchanged. To put a literal $ in the replacement, write \$.
Examples
Section titled “Examples”Add an auth header to every request
Section titled “Add an auth header to every request”Send a test token to your staging API without changing the app.
| Matcher | Action |
|---|---|
ANY staging.example.com/* |
Request header · Set · Authorization → Bearer sk_test_xxxx |
Set replaces any Authorization header the app already sends. Use Add to send a second header with the same name instead.
Strip a tracking parameter
Section titled “Strip a tracking parameter”Remove utm_source and utm_campaign before requests reach the server, for example so cached responses match.
| Matcher | Actions |
|---|---|
ANY api.example.com/* |
Query parameter · Remove · utm_source |
Query parameter · Remove · utm_campaign |
Each action removes one parameter name. Add one action per parameter.
Force an error to test your error screen
Section titled “Force an error to test your error screen”| Matcher | Actions |
|---|---|
GET https://api.example.com/v1/users |
Response status · Set · 503 |
Response header · Set · Retry-After → 30 |
The request still reaches the server, and the app receives the server’s body with status 503. To answer without contacting the server at all, use Map Local instead.
Turn on a feature flag
Section titled “Turn on a feature flag”Change a flag in a JSON response so the app shows a feature that’s off on the server.
| Matcher | Action |
|---|---|
GET https://api.example.com/v1/flags |
Response body · Find and replace · "newCheckout": false → "newCheckout": true |
Find and replace works on the body’s text, so Find must match the spacing the server uses. If the server sends "newCheckout":false without a space, match that, or use Regex with "newCheckout":\s*false.
Point the app at a new API version
Section titled “Point the app at a new API version”| Matcher | Action |
|---|---|
ANY api.example.com/v1/* |
URL · Find and replace · /v1/ → /v2/ |
This changes the path but not the server. A URL action doesn’t update the Host header, so to send requests to a different host, use Map Remote, which does.
When rewrites run
Section titled “When rewrites run”Rewrite works on HTTP traffic Cloak Proxy can read: plain HTTP, and HTTPS with SSL Proxying on. It changes the request on the way out and the response on the way back:
| Side | Runs | So |
|---|---|---|
| Request actions (Request header, Query parameter, URL, Request body) | Right after the Block List, before Network Conditions, request scripts and the request breakpoint. | Every later tool, including Map Local, Mock Sets and Map Remote, sees the rewritten request. A paused request already shows your rewrites. |
| Response actions (Response header, Response body, Response status) | After the response arrives (and after No Caching strips its headers), before response scripts and the response breakpoint. | Response scripts and a paused response see the rewritten response. |
More details:
- Rules match the request as the app sent it, for both the request and the response actions. A rule whose URL action changes the path still applies its response actions.
- Every enabled rule that matches applies, from the top of the list to the bottom, and each rule’s actions run in order. Each one works on the result of the one before.
- Response actions also apply to responses from Map Local and Mock Sets. They don’t apply to the
403the Block List sends, or to a request aborted at a breakpoint. - Repeat, Edit and Repeat and Compose requests go through Rewrite like live traffic.
- The traffic list records the request as it was finally sent and the response as the app received it.
The full order of every tool is in What happens when several tools match.
Compressed and binary bodies
Section titled “Compressed and binary bodies”Before a Request body or Response body action runs, Cloak Proxy decompresses a gzip, deflate or Brotli body and removes the Content-Encoding header, so Find sees readable text. When the rule changes anything, the body is sent on uncompressed. When it changes nothing, the original body is sent as it was. Bodies are only decompressed for rules that have a body action.
A body that isn’t UTF-8 text, such as an image, is left unchanged. Cloak Proxy recalculates Content-Length after any change.
Rewrite, Scripting or Map Remote?
Section titled “Rewrite, Scripting or Map Remote?”All three change traffic. Pick the simplest one that does the job.
| Use | When you want to | Examples |
|---|---|---|
| Rewrite | Make fixed edits you can describe as “set this”, “remove that” or “replace this text”. | Add a header, drop a parameter, force a status, flip a flag in a body. |
| Scripting | Decide in code: edit JSON fields as objects, compute values, change the method, keep state between requests, or act only sometimes. | Fail every third request, sign a body, reuse a token from an earlier response. |
| Map Remote | Send requests to a different server, with the Host header updated to match. |
Production to staging, or to localhost:3000. |
Rewrite runs before scripts, so a script sees what Rewrite changed. You can combine them: a Rewrite rule for the fixed headers, and a script for the logic.
Limit a rule to one app
Section titled “Limit a rule to one app”The Only these apps field under the URL limits a rule to requests from the apps you list, by name as shown in the sidebar, separated by commas. Leave it empty to match every app. For example, MyApp adds a debug header to your app’s requests to api.example.com but not to the same requests from Safari. See Limit a rule to apps.
Export and use rules elsewhere
Section titled “Export and use rules elsewhere”- Tools › Export Rules… includes your Rewrite rules in the JSON file, under
rewrites, with the on/off switch inrewritesEnabled. - Tools › Import Rules… adds the Rewrite rules from a file to yours; a rule with the same
idreplaces the existing one. Your Rewrite switch stays as it is. cloakctl run --tools RULES.jsonapplies the Rewrite rules in an exported file to a headless run.
Create rewrite rules from an AI assistant
Section titled “Create rewrite rules from an AI assistant”The MCP server manages Rewrite rules like any other tool:
list_ruleswithtoolset torewritelists the rules, whether the tool is on, and an example rule with every field.upsert_rulewithtoolset torewritecreates a rule, or changes one when you pass itsid. It turns Rewrite on.rule_from_requestwithtoolset torewritecreates an empty rule matching a captured request.set_tool_enabledanddelete_ruleturn the tool on or off and delete a rule.
An upsert_rule call that adds the auth header and strips a tracking parameter for one app:
{ "tool": "rewrite", "rule": { "name": "Staging auth", "matcher": { "url": "staging.example.com/*", "method": "ANY", "apps": ["MyApp"] }, "actions": [ { "target": "requestHeader", "operation": "set", "name": "Authorization", "value": "Bearer sk_test_xxxx" }, { "target": "queryParameter", "operation": "remove", "name": "utm_source" }, { "target": "responseBody", "operation": "replace", "find": "\"beta\": false", "value": "\"beta\": true" }, { "target": "status", "operation": "set", "value": "503", "isEnabled": false } ] }}The action fields:
| Field | Values | Default |
|---|---|---|
target |
requestHeader, queryParameter, url, requestBody, responseHeader, responseBody, status |
Required |
operation |
set, add, remove, replace |
The target’s first operation: set for headers, parameters and the status, replace for the URL and bodies |
name |
Header or parameter name | "" |
value |
The new value, the replacement text, or the status code as a string | "" |
find |
The text or regular expression to find, for replace |
"" |
isRegex |
true or false |
false |
isEnabled |
true or false |
true |
See the MCP tools reference for every argument.