※この記事の情報はFirefox ESR52を対象としています。これより新しいバージョンではこれらの情報は当てはまらない場合がありますので、ご注意下さい。
Firefoxの先読み機能の種類と無効化の方法
FirefoxにはWebサイトの閲覧時の体感的な快適さを向上するため、実際にリクエストが行われる前から接続やデータのダウンロードを行っておくという「先読み」の機能があります。ただし、無差別に全てのリンクを辿るのではなく、いくつかの条件が揃った時に初めて先読みが発動するようになっています。具体的には以下の要領です。
-
ブログの前後のエントリのように、続けて読まれる可能性が高いリンク
-
HTTPヘッダやリンクの属性値などによって明示的に先読みするよう指示された対象(ログインページを表示している間に、ログイン後のページで使用するための画像などを先読みする、など)
-
多数の読み込みが並行して進行している場面での、読み込み待ちのリクエストに対するDNSでの名前解決のみの先行着手(DNSプリフェッチ)
-
ユーザーがマウスで指し示したリンクのリンク先ホストへの、ソケット接続のみの先行着手(リンクの上にポインタが移動してからクリックされるまでの時間を利用した投機的接続)
ユーザーから見れば快適さが増して嬉しい機能ですが、反面、ネットワークのトラフィックやセッション数は増大する事になります。弊社のMozillaサポート事業のお客様からも、契約回線の帯域が限られているなどの理由で、情報システム管理担当者の意向として「先読み機能を無効化したい」というご相談を頂く事があります。また、「既に担当者側でそのような設定を行ったつもりだが、実際にその指定が機能しているかどうかを確認したい」というご相談もあります。
前述の先読みに類する機能を全て無効化する場合、指定は以下のようになります。
- 全般的な制御
network.predictor.enabled
=false
(真偽型)network.predictor.enable-hover-on-ssl
=false
(真偽型)network.predictor.enable-prefetch
=false
(真偽型)
- 次のページを示すリンクの先読み
network.prefetch-next
=false
(真偽型)
- DNSプリフェッチ
network.dns.disablePrefetch
=true
(真偽型)network.dns.disablePrefetchFromHTTPS
=true
(真偽型):TLSを使用したページでの挙動の制御。
- リンクの上にポインタが載った時の投機的接続の抑止
network.http.speculative-parallel-limit
=0
(整数型)
では、これらの設定が期待通りに反映されている事をどのように確認すればよいのでしょうか。これがこの記事の本題です。
設定が反映されているかどうかの確認
設定の書き間違いのようなケアレスミスや、それ以外にも何らかの理由から、設定したはずの値が反映されないままになっているという事は度々あります。そのため、この手のカスタマイズはなるべく、実際の挙動から設定の反映状況を確認する事が望ましいです。
データのダウンロードまでも行う場合の確認
HTTP接続からデータのダウンロードまでを行うフルスペックの「先読み」については、先読み対象となるHTTPリクエストが実際に処理されたかどうかを見るのが確実です。例えばこのブログの2016年5月10日の記事は同年5月18日の記事へのリンクを含んでいますが、このリンクにはrel="next"
という属性が指定され次のページへのリンクである事が示されているため、先読みの対象となります。
このような先読みは、ブラウザコンソールで動作を確認できます。
キーボードショートカットの「Ctrl-Shift-J」を使うかパネルメニューの「開発ツール」(またはメニューバーの「Web開発」)で「ブラウザコンソール」をクリックするとブラウザコンソールのウィンドウが開かれますので、そのウィンドウ上部の「ネットワーク」がハイライトされている状態でこのブログの2016年5月10日の記事を開いてみて下さい。先読みが機能していればGET http://www.clear-code.com/blog/2016/5/18.html
というメッセージがコンソールに出力され、機能していなければこのメッセージは出力されません。
コンソールに出力される出力が多すぎて見分けが難しいといった場合には、低レベルのログでも動作を確認できます。この方法については後述します。
DNSの名前解決のみ行われる場合の確認
先読み機能には、DNSでの名前解決のみを先行して行う機能(DNSプリフェッチ)もあります。例えば、ページ内に<link rel="dns-prefetch" href="http://dns-prefetch.example.com">
のような記述が含まれていると、FirefoxはHTTPでのリクエストではなく、dns-prefetch.example.com
の名前解決の問い合わせのみを単独で行います。
この機能が動作しているかどうかを確認するためは、DNSへの問い合わせを行うモジュールが内部的に発行するイベントを監視する必要があります。この確認にもブラウザコンソールを使います。
そのためには、about:config
で以下のように設定します。
devtools.chrome.enabled
=true
(真偽型):スクリプトの実行を可能にする。network.dns.notifyResolution
=true
(真偽型):DNSでの名前解決時に内部的なイベントを発行するようにする。
上記の設定を反映した状態でブラウザコンソールを開くと、コンソール下部に入力欄が出現します。この入力欄に Services.obs.addObserver(function(aSubject, aTopic, aData) { console.log(aTopic+': '+aData); }, 'dns-resolution-request', false);
と入力してEnterキーを押すと、この内部的なイベントを捉えてコンソールにメッセージとして出力する事ができます。DNSプリフェッチのための指定が含まれているWebページ(テストケース)を開いた時にdns-resolution-request: dns-prefetch.example.com
のようなメッセージがブラウザコンソールに出力されれば、DNSプリフェッチのための指定が処理されたと分かりますし、逆に、そのようなWebページを開いてもこのメッセージがコンソールに出力されなければ、DNSプリフェッチは無効化されていると判断できます。
TCP接続のみ行われる場合の確認
Firefoxの先読み機能の中には、データの読み込みは行わないまでも、ソケット接続のみ確立しておくという機能もあります。リンクの上にポインタが載った時点でまずソケット接続だけ先行して行っておき、クリックされた時にすぐにHTTPのリクエストを送出できるようにするというもので、この機能は投機的接続と呼ばれます。
ソケット接続を確立しただけの段階ではコンソールには何も出力されず、また、DNSプリフェッチのような内部的なイベントも発行されないため、投機的接続が行われているかどうかは、低レベルのログを解析して調べる必要があります。
Firefox 52以降のバージョンでは、ログ収集対象のモジュールやログファイルの出力先は環境変数ではなく設定値で指定します。検証を始めるには、about:config
で以下の設定を作成して下さい。
logging.nsHttp
=5
(整数型)logging.NetworkPredictor
=5
(整数型)logging.config.sync
=true
(真偽型)logging.config.add_timestamp
=true
(真偽型)logging.config.clear_on_startup
=false
(真偽型)logging.config.LOG_FILE
=C:\Users\Public\predict.log
(文字列型)
この状態でFirefoxを再起動すると、logging.config.LOG_FILE
で指定したパスの位置にpredict.log-main.123
のような名前でログファイルが出力され始めます。(main
は親プロセスのログであることを示します。ファイル名の末尾の数字はプロセスIDです。)投機的接続の機能は通常のWebブラウズの中で暗黙的に行われるため、この状態のまましばらくWebページを2〜3画面分ほど見て回り、十分な量のログを溜めて下さい。
2017-06-02 09:51:57.565000 UTC - [Main Thread]: I/Logger Flushing old log files
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp Creating nsHttpHandler [this=9fe1400].
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::Init
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::PrefsChanged [pref=(null)]
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::PrefsChanged Security Pref Changed (null)
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpHandler::MakeNewRequestTokenBucket this=9fe1400 child=0
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpAuthCache::Init
2017-06-02 09:51:57.659000 UTC - [Main Thread]: D/nsHttp nsHttpAuthCache::Init
2017-06-02 09:51:57.659000 UTC - [Main Thread]: V/nsHttp Creating nsHttpConnectionMgr @b5a1420
...
続けて、キーボードショートカットの「Ctrl-Shift-K」を使うかパネルメニューの「開発ツール」(またはメニューバーの「Web開発」)で「Webコンソール」をクリックし、ログファイルを読み込んだタブに対してコンソールを表示します。コンソール下部の入力欄に以下のログ解析用のスクリプトをコピー&ペーストし、Enterキーを押して実行します。
var body = document.body.textContent;
var requested = body.match(/nsHttpConnectionMgr::OnMsgSpeculativeConnect/g) || [];
var skipped = body.match(/Transport not created due to existing connection count/g) || [];
!requested.length ? 'not working' : requested.length == skipped.length ? 'all skipped' : 'preconnected';
すると、以下の3つのいずれかの解析結果がコンソールに出力されます。
preconnected
:投機的接続が行われた。all skipped
:投機的接続が無効化されている(もしくは、同時接続数が最大値に達しているため投機的接続が行われなかった)。not working
:ネットワーク環境の都合等で投機的接続が行われていない。
3番目の「投機的接続が行われていない」というケースは、そのクライアントが接続しているネットワーク環境によって発生し得ます。Firefoxは投機的接続を開始する前に接続先ホストがプライベートなネットワークに属しているかどうかを見ており、DNSで名前解決した結果が192.168.1.10
や10.21.0.100
といったプライベートネットワーク上のIPアドレスだった場合には投機的接続の処理を中断するようになっています。これは、投機的接続は元々、遠隔地にあるホストとのソケット接続の確立に時間がかかるためにフライングで接続しておくという趣旨の機能なので、ネットワーク的に近いホストに対しては行う意義が薄いからです。
この状態で、検証に使用できる条件1を満たしている公開のWebページ2の例としてneverssl.comを開きます3。Webページが開かれたら、ページ内にある、ラベル文字列が HSTS
でリンク先が https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
であるリンクの上にポインタ(カーソル)を乗せて、しばらく待ちます。その後、上記のログ出力用の設定をリセットしてFirefoxを再起動し、ログ収集を終了します。
ログ収集が完了したら、ログファイルをテキストエディタやブラウザで開きます。この時、ログには以下のような内容が出力されているはずです。
...
2021-08-24 10:24:25.346000 UTC - [Parent 8764: Main Thread]: E/nsHttp HttpBaseChannel::Init [this=2b134022000]
2021-08-24 10:24:25.346000 UTC - [Parent 8764: Main Thread]: E/nsHttp host=en.wikipedia.org port=-1
2021-08-24 10:24:25.346000 UTC - [Parent 8764: Main Thread]: E/nsHttp uri=https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
2021-08-24 10:24:25.346000 UTC - [Parent 8764: Main Thread]: E/nsHttp nsHttpChannel::Init [this=2b134022000]
...
リンクを実際にはクリックしていないにも関わらず、ログにHTTP接続の記録が出力されていることから、投機的先読み機能の作用によって接続が試行されたと判断できます。
他の先読み機能が働いているかどうかもデバッグ用ログから確認する
なお、前項で取得したログを解析すると、データの読み込みを伴う先読みが行われたかどうかも判別できます。今度は以下の解析用スクリプトをWebコンソールで実行します。ログファイルをFirefoxのブラウザウィンドウで開いた状態で、キーボードショートカットの「Ctrl-Shift-K」を使うかパネルメニューの「開発ツール」(またはメニューバーの「Web開発」)で「Webコンソール」をクリックし、コンソールを表示します。次に、コンソール下部の入力欄に以下のログ解析用のスクリプトをコピー&ペーストし、Enterキーを押して実行します。
var body = document.body.textContent;
var success = body.match(/Predictor::Predict.*\n.*called on parent process.*\n.*not enabled/g) || [];
var all = body.match(/Predictor::Predict.*\n.*called on parent process/g) || [];
(success.length == all.length) ? 'all canceled' ? 'some canceled';
出力結果は以下の2つのうちいずれかです。
all canceled
:先読み処理が無効化されている。some canceled
:先読み処理が有効である。
まとめ
以上、Firefox ESR52において先読みに類する機能の無効化の方法と、その検証方法をご案内しました。
ここで解説している情報の中には、まとまった情報源が無く、お客様のご要望を受けて調査した結果判明したという情報も含まれています。クリアコードではOSSのドキュメント化されていない仕様を調査して明らかにする業務も行っておりますので、導入をお考えのOSSが想定通りの動作をするかどうかに不安がある場合や、既にお使いのOSSが期待通りに動作せずお困りの場合などには、お気軽にお問い合わせ下さい。
-
Firefoxの投機的なリンク先接続機能は、初期状態ではHTTPでの通信時にのみ動作します。そのため、検証には「HTTPで接続可能で、且つ、ページ内に外部Webサイトへのリンクが含まれているページ」を使う必要があります。 ↩
-
Firefoxは投機的接続を開始する前に接続先ホストがプライベートなネットワークに属しているかどうかを見ており、DNSで名前解決した結果が
192.168.1.10
や10.21.0.100
といったプライベートネットワーク上のIPアドレスだった場合には投機的接続の処理を中断するようになっています。これは、投機的接続は元々、遠隔地にあるホストとのソケット接続の確立に時間がかかるためにフライングで接続しておくという趣旨の機能なので、ネットワーク的に近いホストに対しては行う意義が薄いからです。 ↩ -
neverssl.comは、フリーWi-Fiスポットなどに接続した際の認証の仕組みである「Captive Portal」の制限を回避するために運用されているWebサイトです。Captive Portalでは通常、最初に行われたHTTPでの通信をアクセスポイント側で乗っ取って、本来のWebページに代わってそのWi-Fiスポットの認証用Webページを返却するようになっています。このとき、電子署名の技術的な性質のため、HTTPSでのリクエストの内容を、ブラウザに対してエラーや警告が表示されない形で乗っ取ることはできず(Webサイトを詐称する中間者攻撃と判断される)ため、このときには必ずHTTPで通信するWebページを使う必要があります。いわゆる「常時SSL化」の風潮により多くのWebサイトがHTTPSでのアクセスのみに移行してしまった結果、Captive Portalのために利用できる公知のWebサイトが減ってしまったことから、このようなWebサイトが用意されたようです。なお、同様の目的でMozillaも公式にdetectportal.firefox.comというサイトを運用していますが、こちらはWebページを返却する物ではないことから、今回の検証には使用できません。 ↩