Fiddler は、ブラウザーとサーバーのあいだに入って通信を記録・変更できる、デバッグ用のプロキシ (通信を中継するソフト) です。その AutoResponder という機能を使うと、特定の URL への要求に、サーバーの代わりに手元のファイルを返せます。公開中のサイトの JavaScript を、手元で直したファイルに差し替えて試す、といった使い方ができます。ところが、ルールを正規表現で書いたとき、URL にクエリ (?v=123 のような部分) が付いていると一致せず、差し替わらないことがあります。

Fiddler には、Windows 向けの Fiddler Classic と、あとから出た Fiddler Everywhere があり、画面や機能の名前が違います。ここでは Fiddler Classic の AutoResponder を例に説明します。細かな動きは版によって違うことがあるので、使っている版の説明もあわせて確かめてください。

ルールの書き方による違い

AutoResponder のルールは、「どの URL に一致したら」と「何を返すか」の組です。公式の資料によると、一致の条件は書き方によって次のように扱われます。

  • 文字列だけを書く (example.com/js/app.js): URL のどこかにその文字列が含まれていれば一致します。大文字と小文字は区別しません
  • EXACT: を付ける: URL 全体が完全に同じときだけ一致します。大文字と小文字を区別します
  • REGEX: を付ける: 正規表現で判定します。資料の例では、大文字と小文字を区別しています

資料の例では、スキーム (https://) からクエリまでを含む URL 全体と照らし合わせています。そのため、EXACT: のルールは、クエリが付いた URL には一致しません。

REGEX: のルールが一致しない原因

$ で終わりを固定している

REGEX:^https://example\.com/js/app\.js$

^ は文字列の先頭、$ は末尾を表します。ブラウザーが要求する URL が https://example.com/js/app.js?v=123 だと、app.js のあとに ?v=123 が続くので、$ の位置で一致しません。キャッシュを避けるために、読み込むファイルの URL にクエリを付けているサイトは多いので、よく起きます。

? や . をエスケープしていない

正規表現では、? は「直前の文字が 0 回か 1 回」、. は「任意の 1 文字」という意味です。URL をそのまま貼り付けて、次のように書くと、? が文字として扱われません。

REGEX:https://example.com/js/app.js?v=123

この正規表現の js? は「j のあとに s があってもなくてもよい」という意味になり、URL の中の ? という文字には一致しません。そのため、app.js?v=123 という URL に一致しなくなります。文字としての ? や . は、\?、\. のように \ を付けて書きます。. はエスケープしなくても「任意の 1 文字」として一致してしまうので気づきにくいのですが、意図しない URL にまで一致する原因になります。

大文字と小文字が違う

URL に大文字が混じっていると、小文字で書いたルールに一致しないことがあります。区別したくないときは、REGEX:(?i)^https://... のように、正規表現の先頭に (?i) を付けます。

クエリがあってもなくても一致させる

REGEX:^https://example\.com/js/app\.js(\?.*)?$

(\?.*)? は、「? から始まる部分が、あってもなくてもよい」という意味です。これで、app.js にも app.js?v=123 にも一致し、app.json や app.js.map には一致しません。

フォルダーの中の .js をまとめて差し替えたいときは、パスの部分に .* ではなく、「? 以外の文字の並び」を表す [^?]* を使うと、範囲を絞れます。

REGEX:^https://example\.com/js/[^?]*\.js(\?.*)?$

.* で書くと、https://example.com/js/load.php?file=a.js のように、クエリの中に .js がある URL にも一致してしまいます。

なお、.* は「できるだけ長く」一致させ (最長一致)、.*? は「できるだけ短く」一致させます (最短一致)。一致するかどうかだけを判定する場面では、この違いで結果が変わることはあまりありません。違いが出るのは、一致した部分のどこまでを取り出すかが問題になるときです。一致しない原因を探すときは、まず $ とエスケープを疑いましょう。

一致するかを確かめる

ルールを直したら、実際の URL で一致を確かめます。Fiddler の通信の一覧から、対象の要求の URL をコピーしておくと確実です。

Fiddler Classic は .NET で作られていて、正規表現も .NET のものです。同じ .NET の正規表現を使う PowerShell で試すと、手軽に確かめられます。

$pattern = '^https://example\.com/js/app\.js(\?.*)?$'
[regex]::IsMatch('https://example.com/js/app.js?v=123', $pattern)  # True
[regex]::IsMatch('https://example.com/js/app.json', $pattern)      # False

正規表現が正しいのに差し替わらないときは、次の点も見直します。

  • HTTPS の通信を復号する設定 (Decrypt HTTPS traffic) が有効か。無効だと HTTPS の要求の中身が見えないため、URL によるルールが働きません
  • ブラウザーのキャッシュから読み込まれていないか。要求そのものが送られないと、ルールは働きません。開発者ツールでキャッシュを無効にして読み込み直します
  • 目的のルールが有効になっているか。より上にある別のルールが、先に一致していないか
  • 一致しなかった要求を本来のサーバーへ通す設定 (Unmatched requests passthrough) が外れていないか。外れていると、ルールに一致しなかった要求がサーバーに届かず、ページの一部がエラーになることがあります

開発者ツールのオーバーライドでもできる

手元のファイルに差し替えるだけなら、今はブラウザーの開発者ツールでもできます。Chrome の開発者ツールの「ローカルでのオーバーライド (Local overrides)」では、Network パネルで要求を右クリックし、「Override content」を選びます。保存先のフォルダーを選ぶと、応答の内容を手元で書き換えられ、ページを読み込み直しても、書き換えた内容が使われます。応答のヘッダーを変えることもできます。

正規表現で多くの URL をまとめて差し替えたい、ブラウザー以外のアプリの通信も扱いたい、といった場合は、Fiddler のようなプロキシが向いています。目的に合わせて使い分けましょう。正規表現の書き方は、正規表現のタグ の記事も参考にしてください。

まとめ

  • REGEX: のルールの末尾に $ を付けると、クエリ付きの URL に一致しない。(\?.*)?$ でクエリを許す
  • URL の ? や . は、正規表現では \?、\. とエスケープする
  • 大文字と小文字を区別したくないときは (?i) を付ける
  • 一致は PowerShell の [regex]::IsMatch() などで確かめ、HTTPS の復号やキャッシュも見直す
  • 手元のファイルに差し替えるだけなら、ブラウザーの開発者ツールのオーバーライドも使える

参考