document.write() に対する介入

最近、Chrome の Developer Console に次のような警告が表示されました。

(index):34 A Parser-blocking, cross-origin script,
https://paul.kinlan.me/ad-inject.js, is invoked via document.write().
This may be blocked by the browser if the device has poor network connectivity.

コンポーザビリティはウェブの大きな強みの 1 つであり、サードパーティが構築したサービスと簡単に統合でき、優れた新サービスを構築できます。コンポーザビリティの欠点の一つは、ユーザー エクスペリエンスに対して責任が共有されることになるということです。統合が最適でないと、ユーザー エクスペリエンスに悪影響が及びます。

パフォーマンス低下の原因の一つとして、ページ内で document.write() が使用されていることが挙げられます。特に、スクリプトを注入する用途で使用されています。次のように見えるように無害ですが、これはユーザーに重大な問題を引き起こす可能性があります。

document.write('<script src="https://example.com/ad-inject.js"></script>');

ブラウザはページをレンダリングする前に、HTML マークアップを解析して DOM ツリーを構築する必要があります。パーサーは、スクリプトを検出するたびに、HTML の解析を続行する前に、スクリプトを停止して実行する必要があります。スクリプトが別のスクリプトを動的に挿入すると、パーサーはリソースがダウンロードされるまでさらに長い時間待機しなければならず、これによって 1 つ以上のネットワーク ラウンドトリップが発生し、ページの最初のレンダリングまでの時間が遅れる可能性があります。

2G などの低速な接続を使用するユーザーの場合、document.write() を介して動的に挿入された外部スクリプトにより、メインページのコンテンツの表示が数十秒遅れたり、ページの読み込みに失敗したり、ユーザーがあきらめたりするほど時間がかかる可能性があります。Chrome での測定に基づいて、document.write() を介して挿入されたサードパーティ スクリプトを含むページは、通常、2G で他のページより 2 倍遅いことがわかっています。

Google では、Chrome Stable ユーザーの 1% を対象として、2G 接続のユーザーに限定した 28 日間のフィールド トライアルからデータを収集しました。2G でのすべてのページの読み込みの 7.6% に、最上位ドキュメントの document.write() を介して挿入されたクロスサイト パーサー ブロック スクリプトが 1 つ以上含まれていることがわかりました。これらのスクリプトの読み込みをブロックした結果、読み込みに関して以下の改善が見られました。

  • First Contentful Paint に達するページ読み込みが 10% 増加し(ページが効果的に読み込まれていることをユーザーに視覚的に確認できる)、完全解析された状態に達するページの読み込みが 25% 増加し、再読み込みの回数が 10% 減少し、ユーザーの不満が軽減されます。
  • First Contentful Paint までの平均時間を 21% 短縮(1 秒以上高速化)
  • ページの解析にかかる平均時間が 38% 削減されました。これは 6 秒近く改善され、ユーザーにとって重要な情報を表示するまでの時間が大幅に短縮されました。

このデータを考慮して、Chrome バージョン 55 以降では、Chrome での document.write() の処理方法を変更することにより、この既知の不正なパターンを検出すると、すべてのユーザーに代わって介入します(Chrome のステータスをご覧ください)。具体的には、次の条件をすべて満たしている場合、document.write() によって挿入された <script> 要素は Chrome によって実行されません。

  1. ユーザーの接続速度が遅い場合(特に 2G 接続の場合)。(将来的には、3G や Wi-Fi が遅いなど、接続速度が遅い他のユーザーにも影響が及ぶ可能性があります)。
  2. document.write() はトップレベルのドキュメントにあります。iframe 内の document.writing スクリプトの場合は、メインページのレンダリングをブロックしないため、この介入は適用されません。
  3. document.write() のスクリプトはパーサー ブロックです。async 属性または defer 属性が指定されたスクリプトは引き続き実行されます。
  4. スクリプトが同じサイトでホストされていない。つまり、Chrome は eTLD+1 が一致するスクリプト(www.example.org に挿入された js.example.org でホストされているスクリプトなど)に介入しません。
  5. スクリプトがブラウザの HTTP キャッシュにまだ存在しない。キャッシュ内のスクリプトはネットワーク遅延を発生させず、引き続き実行されます。
  6. ページのリクエストは再読み込みではありません。Chrome はユーザーが再読み込みをトリガーしても介入せず、通常どおりページを実行します。

サードパーティ スニペットでは、document.write() を使用してスクリプトを読み込むことがあります。幸いなことに、ほとんどのサードパーティは非同期読み込みの代替手段を提供しています。これにより、ページ上の他のコンテンツの表示を妨げることなくサードパーティのスクリプトを読み込めます。

どうすればよいですか?

この単純な答えは、document.write() を使用してスクリプトを挿入しないことです。非同期ローダーをサポートする既知のサービスがありますので、継続的に確認することをおすすめします。

ご利用のプロバイダがこのリストになく、非同期スクリプト読み込みをサポートしている場合は、Google までお知らせください。ページを更新いたします。これにより、すべてのユーザーをサポートできます。

プロバイダがページに非同期でスクリプトを読み込む機能をサポートしていない場合は、プロバイダに連絡し、影響を Google に知らせていただくことをおすすめします。

プロバイダから document.write() を含むスニペットが提供されている場合は、script 要素に async 属性を追加するか、document.appendChild()parentNode.insertBefore() などの DOM API を使用してスクリプト要素を追加できる場合があります。

サイトが影響を受けた場合を検出する方法

制限が適用されるかどうかは、多くの基準によって決まりますが、影響を受けるかどうかを確認するには、どうすればよいでしょうか。

ユーザーが 2G 接続中であることを検出する

この変更の潜在的な影響を理解するには、まず 2G に移行するユーザーの数を把握する必要があります。Chrome で利用可能な Network Information API を使用して、ユーザーの現在のネットワークの種類と速度を検出し、分析システムまたは実ユーザー指標(RUM)システムにヘッドアップを送信できます。

if(navigator.connection &&
    navigator.connection.type === 'cellular' &&
    navigator.connection.downlinkMax <= 0.115) {
    // Notify your service to indicate that you might be affected by this restriction.
}

Chrome DevTools で警告をキャッチ

Chrome 53 以降、DevTools で問題のある document.write() ステートメントに関する警告が表示されます。具体的には、document.write() リクエストが条件 2 ~ 5 を満たす場合(Chrome はこの警告を送信する際に接続条件を無視します)、次のような警告が表示されます。

ドキュメントの書き込みに関する警告。

Chrome DevTools に警告が表示されるのは便利ですが、この警告を大規模に検出するにはどうすればよいでしょうか。介入の発生時にサーバーに送信される HTTP ヘッダーを確認できます。

スクリプト リソースの HTTP ヘッダーを確認する

document.write を介して挿入されたスクリプトがブロックされると、Chrome はリクエストされたリソースに次のヘッダーを送信します。

Intervention: <https://shorturl/relevant/spec>;

document.write を使用して挿入したスクリプトが検出され、さまざまな状況でブロックされる可能性がある場合、Chrome から次のメッセージが送信されることがあります。

Intervention: <https://shorturl/relevant/spec>; level="warning"

介入ヘッダーは、スクリプトの GET リクエストの一部として送信されます(実際の介入の場合は、非同期で)。

未来に待ち受けているものとは

最初の計画では、満たされている条件を検出したときにこの介入を実行します。私たちは、Chrome 53 のデベロッパー コンソールに警告だけを表示することから始めました。 (ベータ版は 2016 年 7 月にリリースされました。Stable は 2016 年 9 月からすべてのユーザーが利用できるようになる予定です)。

Google は、Chrome 54(2016 年 10 月中旬にすべてのユーザーの安定版としてリリースされる予定)以降、2G ユーザー向けのスクリプトの挿入を暫定的にブロックします。最新情報については、Chrome のステータス エントリをご覧ください。

徐々に、ユーザーが接続速度が遅い場合(3G や Wi-Fi が遅いなど)に介入しようとします。こちらの Chrome のステータス エントリに沿って対応します。

もっと詳しくお知りになりたい場合は、

詳しくは、以下の参考情報をご覧ください。