Proxyman の Scripting で WebSocket メッセージを変更する方法
6 分で読めます
Proxyman で WebSocket メッセージをその場で変更 できるようになりました 🎉
JavaScript を数行書くだけで、メッセージを編集したり、破棄したり、新しいメッセージを送信したりできます。WS と WSS に対応しており、クライアントからサーバーへ、サーバーからクライアントへ、どちらの方向でも扱えます。
このブログでは次の内容を扱います:
- WebSocket アプリのテストにおける課題。
- 新しい Modify WebSocket Messages オプションの動作。
- 自分で実行できるローカル WebSocket サーバーを使った、ステップごとのデモ。
- すぐに使える WebSocket テンプレートと、変更が反映されないときのヒント。
Modify WebSocket Messages は Proxyman for macOS の新機能です。このガイドを試すには、ダウンロードページ から最新バージョンにアップデートしてください。
完成イメージは次のとおりです。サーバーは実際の BTC 価格を送信していますが、アプリが受け取るのは $1 です。アプリがパスワードを送信すると、Proxyman はそのメッセージを破棄し、警告をアプリに返します:
1. 課題
チャット、取引、ゲーム、ライブダッシュボードなどのアプリは、リアルタイムデータの取得に WebSocket を使います。これらのテストは難しく、理由は次のとおりです:
- サーバーを制御できません。 サーバーは実際のデータを送信します。価格が 0 になる場合やエラーメッセージのような特殊なケースを送らせるには、サーバーのコードを変更する必要があります。
- 再現しにくいケースがあります。 メッセージの欠落、奇妙な応答、接続の切断は、必要なときにはめったに起きません。
- これまでの Proxyman は WebSocket メッセージを読むことしかできませんでした。 Scripting の
onWebsocket()関数でメッセージをログに出力できましたが、変更はできませんでした。
2. 「Modify WebSocket Messages」とは
これは Scripting ルールの新しいチェックボックスです。オンにすると、Proxyman は onWebsocket() 関数が値を返すまで、各 WebSocket メッセージを保留します。何を返すかによって結果が決まります:
onWebsocket() 内の記述 | 結果 |
|---|---|
return message | 変更を加えたメッセージを送信 |
return null | メッセージを破棄。相手側には届かない |
| 戻り値なし | 元のメッセージを送信 |
abort() | 両側で WebSocket を閉じる |
sendToClient(data) / sendToServer(data) | 新しいメッセージを送信。String を渡すとテキストメッセージ、Uint8Array を渡すとバイナリメッセージになる |
message オブジェクトには次のフィールドがあります:
| フィールド | 値 |
|---|---|
message.from | "client" または "server" |
message.type | "text"、"binary"、"ping"、"pong"、または "close" |
message.data | メッセージのテキスト。テキストメッセージを編集するには、この値を変更します |
message.rawData | バイナリメッセージのバイト列(Uint8Array) |
このオプションはデフォルトでオフです。オフのときは、onWebsocket() は従来どおり動作します。メッセージを読み取るだけで、遅延させることはありません。
3. 手順:ライブ WebSocket を変更する
動画のデモを作っていきます。BTC 価格を送信する小さなサーバーと、Proxyman を経由してそのサーバーとやり取りするクライアントです。
3.1 WebSocket サーバーを起動する
新しいフォルダを作成し、ws パッケージをインストールします:
mkdir websocket-demo && cd websocket-demo
npm init -y
npm install ws
次の内容を server.js として保存します。1 秒ごとに価格を送信し、受け取ったチャットメッセージをすべてエコーバックします:
// server.js: a WebSocket server on ws://localhost:3456
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 3456 });
server.on('connection', (socket) => {
// Send a BTC price every second
const timer = setInterval(() => {
const price = Number((67000 + Math.random() * 1000).toFixed(2));
socket.send(JSON.stringify({ type: 'price', symbol: 'BTC', price: price }));
}, 1000);
// Print what the client sends, then echo it back
socket.on('message', (data) => {
console.log('SERVER gets:', data.toString());
socket.send(JSON.stringify({ type: 'echo', text: JSON.parse(data).text }));
});
socket.on('close', () => clearInterval(timer));
});
console.log('WebSocket server on ws://localhost:3456');
ターミナルで実行します:
node server.js
3.2 Proxyman を経由してクライアントを接続する
Node.js は macOS のプロキシ設定を使わないため、クライアントは WebSocket のハンドシェイクを Proxyman に直接送信します。次の内容を client.js として保存します:
// client.js: connects to the server through Proxyman
const http = require('http');
const WebSocket = require('ws');
// Proxyman's port. You can see it in the Proxyman toolbar
const PROXYMAN_PORT = 9090;
// Sends the WebSocket handshake to Proxyman, like an app that uses an HTTP proxy
class ProxymanAgent extends http.Agent {
addRequest(request, options) {
request.path = 'http://' + options.host + ':' + options.port + request.path;
super.addRequest(request, { ...options, host: '127.0.0.1', port: PROXYMAN_PORT });
}
}
const socket = new WebSocket('ws://localhost:3456/prices', { agent: new ProxymanAgent() });
socket.on('open', () => {
setTimeout(() => socket.send(JSON.stringify({ type: 'chat', text: 'Hello' })), 2000);
setTimeout(() => socket.send(JSON.stringify({ type: 'chat', text: 'my password is hunter2' })), 4000);
});
socket.on('message', (data) => console.log('CLIENT gets:', data.toString()));
Proxyman を開き、別のターミナルでクライアントを実行します:
node client.js
Proxyman に ws://localhost:3456/prices が表示されます。Websocket タブを開くと、メッセージを確認できます。まだ何も変更されていません。Ctrl+C でクライアントを停止します。
3.3 Scripting ルールを作成する
- Proxyman で
ws://localhost:3456/pricesのフローを右クリックし、Tools → Scripting... を選択します。Proxyman が URL を入力済みのルールを作成します。 - Request はオンのままにして、Modify WebSocket Messages にチェックを入れます。
- スクリプトを下のコードに置き換えて、Save & Activate(⌘S)を押します。

function onWebsocket(context, url, request, response, message) {
// Only change text messages. Binary, ping, pong, and close messages pass through
if (message.type !== "text") {
return message;
}
const json = JSON.parse(message.data);
// 1. Server → client: every BTC price becomes $1
if (message.from === "server" && json.type === "price") {
json.price = 1;
json.note = "edited by Proxyman";
message.data = JSON.stringify(json);
return message;
}
// 2. Client → server: drop a message with a password, and warn the client
if (message.from === "client" && json.text.includes("password")) {
sendToClient(JSON.stringify({ type: "warning", text: "Proxyman blocked a password" }));
return null;
}
// 3. Everything else: send it as it is
return message;
}
3.4 再実行して結果を確認する
node client.js をもう一度実行します。Proxyman はこのオプションを WebSocket の接続時に判断するため、ルールを保存したら必ず再接続してください。
これで、クライアントは "price": 1 と、note "edited by Proxyman" を含むメッセージを受け取ります。パスワードのメッセージはサーバーに届きません。サーバー側のターミナルには Hello だけが表示され、代わりにクライアントはスクリプトからの警告を受け取ります。

Proxyman の Websocket タブには、編集された価格、スクリプトが送信した警告、そして破棄されたパスワードのメッセージが表示されます。

4. その他の例
テキストを両方向で置き換えます(例:ホスト名の切り替え):
const FIND_REGEX = /staging\.example\.com/g;
const REPLACE_WITH = "api.example.com";
function onWebsocket(context, url, request, response, message) {
if (message.type === "text") {
message.data = message.data.replace(FIND_REGEX, REPLACE_WITH);
}
return message;
}
10 件のメッセージの後に接続切れをシミュレートし、アプリの再接続の挙動をテストします。関数の外にあるコードは接続ごとに 1 回だけ実行されるため、各接続は独自にメッセージ数を数えます:
const CLOSE_AFTER_MESSAGES = 10;
let messageCount = 0;
function onWebsocket(context, url, request, response, message) {
messageCount += 1;
if (messageCount === CLOSE_AFTER_MESSAGES) {
console.log("Closing the WebSocket after " + messageCount + " messages");
abort();
}
return message;
}
バイナリメッセージを変更するには、新しいバイト列を設定します:
function onWebsocket(context, url, request, response, message) {
if (message.type === "binary" && message.from === "server") {
message.rawData = new Uint8Array([1, 2, 3]);
}
return message;
}
5. WebSocket テンプレートですばやく始める
スクリプトをゼロから書く必要はありません。Script Editor で Templates をクリックし、「websocket」を検索します:
- Log WebSocket Messages:すべてのメッセージを Console Log に出力します。メッセージを読み取るだけです。
- Modify JSON WebSocket Messages:サーバーから届く JSON メッセージのフィールドを変更します。
- Replace Text in WebSocket Messages:両方向でテキストを検索して置き換えます。
- Drop WebSocket Messages:正規表現に一致するメッセージ(例:アナリティクスイベント)を破棄します。
- Reply to a WebSocket Message:
sendToClient()を使って、クライアントのメッセージに応答します。サーバーには届きません。 - Simulate a WebSocket Disconnect:数件のメッセージの後に WebSocket を閉じます。
テンプレートを使うには Replace をクリックします。Proxyman は必要なチェックボックスも自動で設定します。その中には Modify WebSocket Messages も含まれます。

6. 問題と解決方法
メッセージが変わらない
- Modify WebSocket Messages にチェックを入れ、Save & Activate(⌘S)を押します。
- WebSocket に再接続します。このオプションは、メッセージごとではなく、WebSocket の接続時に読み込まれます。
- Request か Response のどちらかをオンのままにします。両方がオフのルールは、
onWebsocket()を含めて一切実行されません。 - Script Editor の Console Log を開きます。スクリプトでエラーが発生した場合、Proxyman は元のメッセージを送信し、そこにエラーを出力します。
Proxyman で WebSocket が表示されない
wss://の場合は、HTTPS のときと同様に、そのドメインで SSL Proxying を有効にしてください。- 多くのアプリやライブラリは、
localhostへのアクセスでプロキシを経由しません。client.jsと同じように Proxyman へ通信を送るか、お使いのプラットフォーム向けの WebSocket のドキュメント に従ってください。
知っておくと便利なこと
- ルールに一致する WebSocket だけがスクリプトの処理を待ちます。ほかの通信はそのまま流れ続けます。
- 16 MB を超えるメッセージは、スクリプトを通さずにそのまま送信されます。
- AI エージェントを使っている場合は、Proxyman MCP で同じルールを作成できます。
create_scripting_ruleにmodify_websocket_messages: trueを指定します。
関連情報
