こんにちは 私の名前はアレクサンダーです。私はBadooのフロントエンド開発者です。 おそらく、ここ数年でフロントエンドの世界で最も議論されているトピックの1つは、HTTP / 2プロトコルでしょう。 無駄ではありません-それへの移行は、開発者がサイトをスピードアップして最適化する多くの機会を開くからです。 この投稿は、これらの機能の1つであるサーバープッシュ専用です。 ジェレミー・ワグナーの記事は私にとって興味深く思えたので、有益な情報をあなたと共有しています。
少し前まで、パフォーマンス指向の開発者の能力は著しく変化しました。 そして、HTTP / 2の出現はおそらく最も重要な変化でした。 HTTP / 2は私たちが楽しみにしている機能ではなくなりました-既に存在し(HTTP / 1に存在するキューの先頭や非圧縮ヘッダーのブロックなどの問題にうまく対処します)、サーバープッシュに付属しています!
このテクノロジーにより、ユーザーはサイトリソースを送信する前に送信できます。 これは、埋め込みなどのHTTP / 1最適化方法でパフォーマンスの利点を達成し、このプラクティスに関連する欠点を回避するためのエレガントな方法です。
この記事では、操作の原理から解決する問題まで、サーバープッシュに関するすべてのことを学習します。使用方法、機能するかどうか、パフォーマンスへの影響などを判断する方法などです。
サーバープッシュとは何ですか?
Webサイトへのアクセスは、常に「リクエスト/レスポンス」テンプレートに従って実行されます。ユーザーはリモートサーバーにリクエストを送信します。リモートサーバーは、リクエストされたコンテンツを含むレスポンスを遅延して送信します。
通常、Webサーバーへの最初の要求はHTMLドキュメントを要求します。 サーバーは、要求されたHTMLリソースで応答します。 結果のHTMLドキュメントはブラウザによって分析され、その結果、スタイルシート、スクリプト、画像などの他のリソースへのリンクが抽出されます。 それらが検出された後、ブラウザは各リソースに対して個別のリクエストを送信し、対応するレスポンスを受信します。
このメカニズムの問題は、HTMLドキュメントがロードされた後、ブラウザーが必要なリソースを検出して取得するまでユーザーを待機させることです。 これにより、レンダリングが遅れ、ロード時間が長くなります。
この問題の解決策があります。 サーバープッシュを使用すると、ユーザーが明示的に要求する前に、サーバーがWebサイトリソースをクライアントにプロアクティブに「プッシュ」できます。 つまり、リクエストされたページにユーザーが必要とすることがわかっている情報を事前に送信できます。
すべてのページがstyles.css
と呼ばれる外部スタイルシートで定義されたスタイルに依存しているWebサイトがあるとしstyles.css
。 ユーザーがサーバーからindex.htmlを要求すると、index.htmlの応答の送信を開始した直後にstyles.css
を送信styles.css
。
サーバーがindex.html
送信するまで待機する代わりに、ブラウザがstyles.css
リクエストして受信する間、ユーザーは最初のリクエストに対する回答を待つだけで済みます。 この回答には、 index.html
とstyles.css
両方のファイルが含まれstyles.css
。 これは、ブラウザが待たなければならない場合よりも速くページのレンダリングを開始できることを意味します。
ご覧のとおり、サーバープッシュを使用すると、ページのレンダリング時間を短縮できます。 また、他のいくつかの問題、特にフロントエンド開発に関する問題を解決します。
サーバープッシュはどのような問題を解決しますか?
重要なコンテンツを受信するためのサーバーへの呼び出しの数を減らすことは、サーバープッシュが解決する問題の1つにすぎませんが、決して唯一の問題ではありません。
そのため、サーバープッシュは、CSSとJavaScriptをHTMLに直接埋め込むか、 データURIスキームを使用してバイナリデータをCSSとHTMLに埋め込むなど、HTTP / 1最適化の多くのアンチパターンの適切な代替手段です。 これらのメソッドは、主観的なページ読み込み時間を短縮するため、HTTP / 1の最適化に役立ちます。 つまり、ページの合計読み込み時間は短縮できませんが、ユーザーのページの読み込みは高速になります。
それは確かに理にかなっています。 <style>
タグでHTMLドキュメントにCSSを埋め込むと、ブラウザーは、外部ソースからスタイルが抽出されるのを待たずに、すぐにHTMLにスタイルを適用できます。 この概念は、データURIスキームを使用して、埋め込みスクリプトとバイナリデータの両方で機能します。
これは問題を解決する良い方法のようですよね? HTTP / 1の場合、他に選択肢はありません-もちろんです! しかし、コインの裏側は、埋め込まれたコンテンツを効率的にキャッシュできないことです。 リソース(スタイルシートやJavaScriptファイルなど)が外部でモジュール式のままである場合、はるかに効率的にキャッシュできます。 また、ユーザーが同じリソースを必要とする次のページに移動すると、キャッシュから取得できるため、追加のサーバー要求が不要になります。
ただし、コンテンツを埋め込む場合、独自のキャッシュコンテキストはありません。キャッシュコンテキストは、コンテンツが埋め込まれているリソースと一致します。 たとえば、CSSが埋め込まれたHTMLドキュメントを取り上げます。 HTMLドキュメントのキャッシュポリシーにより、常にサーバーからマークアップの新しいコピーをダウンロードする必要がある場合、埋め込まれたCSSが単独でキャッシュされることはありません。 もちろん、埋め込まれているドキュメントはキャッシュできますが、同じ複製されたCSSを含む他のページはリロードされます。 また、キャッシュポリシーがそれほど厳しくない場合でも、HTMLドキュメントの有効期間は通常限られています。 ただし、これは、HTTP / 1を最適化するときに用意する妥協案です。 これは実際に機能し、サイトを初めて訪れる場合には非常に効果的です。 しかし、第一印象はしばしば決定的です。
これらは、サーバープッシュが処理する問題です。 リソースをプッシュすると、埋め込み時と同じ実用的な利点が得られますが、独自のキャッシュポリシーを持つ外部ファイルにリソースを保存できます。 確かに、このプロセスでは、記事の最後で考慮されるニュアンスが1つあります。 それまでの間、続けましょう。
サーバープッシュの使用を検討する理由について十分に説明し、このテクノロジがユーザーと開発者の両方にとって解決する問題の範囲についても概説しました。 次に、その使用方法について説明しましょう。
サーバープッシュの使用方法
サーバープッシュを使用するには、通常、次の形式のLink
HTTPヘッダーを使用する必要があります。
Link: </css/styles.css>; rel=preload; as=style
通知、私は「通常」と言いました。 上記の内容は、実際にpreload
プリロードリソース preload
ヒントです ( プリロードリソースヒント )。 これはサーバープッシュ以外の個別の最適化ですが、ほとんどの(すべてではない)HTTP / 2実装は、 preload
リソースpreload
を含むLink
ヘッダーで指定されたオブジェクトをプッシュします。 サーバーまたはクライアントがプッシュされたリソースの受け入れを拒否した場合でも、クライアントは指定されたリソースの早期取得を開始できます。
ヘッダーのas=style
部分はオプションです。 プッシュされるコンテンツのタイプについてブラウザに通知します。 この場合、 style
値を使用して、オブジェクトがスタイルシートであることを示します( 他のタイプのコンテンツを指定できます)。 as
値をスキップすると、ブラウザーがプッシュされたリソースを2回ロードする可能性があることに注意することが重要です。 忘れないでください!
プッシュの開始方法がわかったので、 Link
ヘッダーを設定する方法を検討してください。 これを行うには2つの方法があります。
- Webサーバー設定(例:Apacheの
httpd.conf
または.htaccess
) - バックエンド言語関数(たとえば、PHP
header
関数)。
Webサーバー設定でのLink
ヘッダーの設定
HTMLファイルが要求されるたびにスタイルシートをプッシュするように、 httpd.conf
または.htaccess
ファイルを介してApacheサーバーを構成する例を次に示します。
<FilesMatch "\.html$"> Header set Link "</css/styles.css>; rel=preload; as=style" <FilesMatch>
ここでは、 FilesMatch
ディレクティブを使用して、 .html
終わるファイルのリクエストをフィルタリングします。 この基準を満たす要求を受信すると、 Link
ヘッダーを応答に追加し、サーバーに/css/styles.css
リソースをプッシュするよう指示します。
注: Apache HTTP / 2モジュールは、 H2PushResource
ディレクティブを使用してリソースプッシュを開始することもできます。 このディレクティブのドキュメントには、このメソッドがLink
ヘッダーを使用するよりも早くプッシュをトリガーできることが記載されています。 特定のインストールによっては、この機能にアクセスできない場合があります。 この記事で後述するパフォーマンステストでは、 Link
ヘッダーメソッドを使用します。
Nginxは現在HTTP / 2サーバープッシュをサポートしていません。これまでのところ、ソフトウェア変更リストはサポートが追加されたことを示していません。 これは、Nginx HTTP / 2の実装が進化するにつれて変更される可能性があります。
バックエンドコードでのLink
ヘッダーの設定
Link
ヘッダーを設定する別の方法は、サーバー言語を使用することです。 これは、Webサーバーの設定を変更できない場合に役立ちます。 以下は、PHP header
関数を使用してLink
ヘッダーを設定する例です。
header("Link: </css/styles.css>; rel=preload; as=style");
アプリケーションが共有ホスティングにあり、サーバー設定を変更する方法がない場合、この方法が必要です。 このヘッダーを任意のサーバー言語で設定できるはずです。 実行時エラーの可能性を回避するために、応答本文の送信を開始する前にこれを必ず行ってください。
複数のリソースをプッシュする
すべての例は、単一のリソースのプッシュを示しています。 しかし、いくつかをプッシュしたい場合はどうでしょうか? これを行うのは賢明でしょう? 結局のところ、ネットワークとはスタイルシートだけではありません。 方法は次のとおりです。
Link: </css/styles.css>; rel=preload; as=style, </js/scripts.js>; rel=preload; as=script, </img/logo.png>; rel=preload; as=image
複数のリソースをプッシュするには、各プッシュディレクティブをカンマで区切るだけです。 リソースヒントもこの構文を使用してLink
タグを介して追加されるため、プッシュディレクティブを他のリソースヒントと混在させることができます。 preconnect
ヒントをpreconnect
た混合例をpreconnect
ます。
Link: </css/styles.css>; rel=preload; as=style, <https://fonts.gstatic.com>; rel=preconnect
複数のLink
ヘッダーも許可されます。 HTMLドキュメントへのリクエストに複数のLink
ヘッダーを設定するようにApacheを構成する方法は次のとおりです。
<FilesMatch "\.html$"> Header add Link "</css/styles.css>; rel=preload; as=style" Header add Link "</js/scripts.js>; rel=preload; as=script" <FilesMatch>
この構文は、コンマで区切られた複数の値を結合するよりも便利であり、悪化することはありません。 唯一の欠点はコンパクトさの欠如ですが、利便性はネットワークを介して送信される数バイトの余分な価値があります。
リソースをプッシュする方法がわかったので、これが機能するかどうかを判断する方法を見てみましょう。
サーバープッシュが機能しているかどうかを確認する方法
そこで、サーバーに何かをプッシュするように指示するLink
ヘッダーを追加しました。 疑問が残ります。それが機能するかどうかはどうすればわかりますか?
ブラウザによって異なります。 Google Chromeの最近のバージョンでは、プッシュ可能なリソースは、[開発ツール]ウィンドウの[ネットワーク]タブの[イニシエーター]列で識別できます。
さらに、同じタブでWaterfall列にカーソルを合わせると、リソースがプッシュされた時間に関する詳細情報が表示されます。
Mozilla Firefoxのツールは、プッシュ可能なリソースを定義する上であまり明確ではありません。 ブラウザ開発者ツールのネットワークユーティリティ内のそのようなリソースのステータスは、灰色の点でマークされています。
リソースがサーバーによってプッシュされたかどうかを判断する正確な方法を探している場合は、 nghttp
コマンドライン nghttp
を使用して、HTTP / 2サーバーからの応答を確認できます。次に例を示します。
nghttp -ans https://jeremywagner.me
したがって、トランザクションに関係するリソースに関する簡単な情報を受け取ります。 プッシュされたリソースには、たとえば次のようにアスタリスクが付けられます。
id responseEnd requestStart process code size request path 13 +50.28ms +1.07ms 49.21ms 200 3K / 2 +50.47ms * +42.10ms 8.37ms 200 2K /css/global.css 4 +50.56ms * +42.15ms 8.41ms 200 157 /css/fonts-loaded.css 6 +50.59ms * +42.16ms 8.43ms 200 279 /js/ga.js 8 +50.62ms * +42.17ms 8.44ms 200 243 /js/load-fonts.js 10 +74.29ms * +42.18ms 32.11ms 200 5K /img/global/jeremy.png 17 +87.17ms +50.65ms 36.51ms 200 668 /js/lazyload.js 15 +87.21ms +50.65ms 36.56ms 200 2K /img/global/book-1x.png 19 +87.23ms +50.65ms 36.58ms 200 138 /js/debounce.js 21 +87.25ms +50.65ms 36.60ms 200 240 /js/nav.js 23 +87.27ms +50.65ms 36.62ms 200 302 /js/attach-nav.js
ここで、私は自分のサイトでnghttp
を使用しnghttp
た(少なくとも執筆時点では)5つのリソースをプッシュしていました。 関心のあるリソースには、 requestStart
列の左側にアスタリスクが付いています。
リソースがプッシュされるタイミングを判断できるようになったので、サーバープッシュが実際のWebサイトのパフォーマンスにどのように影響するかを見てみましょう。
サーバープッシュパフォーマンス測定
パフォーマンスの改善の効果を測定するには、優れたテストツールが必要です。 Sitespeed.ioはnpmから入手できる素晴らしいツールです。 ページのテストを自動化し、貴重なパフォーマンスメトリックを収集します。
そこで、適切なツールを選択しました-テスト方法論に移ります。
試験方法
サーバープッシュがWebサイトのパフォーマンスに与える影響を測定したかったのです。 結果を関連させるために、6つの別々のシナリオの比較ポイントを設定する必要がありました。 これらのシナリオは、HTTP / 2またはHTTP / 1を使用する2つの側面に分かれています。 HTTP / 2サーバーでは、サーバープッシュの影響をさまざまな方法で測定します。 HTTP / 1サーバーの場合-埋め込みにはサーバープッシュとほぼ同じ利点があるはずなので、リソースの埋め込みがパフォーマンスにどのように影響するかを確認します。
検討中のシナリオ:
- HTTP / 2-拡張なし
この状態では、サイトはHTTP / 2で実行されますが、プッシュされるものはまったくありません。
- HTTP / 2-プッシュCSS
サーバープッシュが使用されますが、CSSサイト専用です。 CSSサイトは非常に小さく、重さは2 KBを超え、Brotliアルゴリズムを使用して圧縮されています 。 - HTTP / 2-すべてをプッシュ
サイトのページで使用されるすべてのリソースがプッシュスルーされます。 これには、CSS、6つのファイルに散在する1.4 KBのJavaScriptコード、および5つのファイルに散在する5.9 KBのSVG画像が含まれます。 すべてのファイルサイズは、圧縮後に示されます(Brotliも使用)。 - HTTP / 1-拡張なし
WebサイトはHTTP / 1プロトコルを使用しており、リクエストの数を減らしたり、レンダリング速度を上げるためのリソースは組み込まれていません。 - HTTP / 1-インラインCSS
すべてのCSSサイトが埋め込まれています。 - HTTP / 1-すべてをインライン
Webサイトのページで使用されるすべてのリソースが埋め込まれています。 CSSとスクリプトは埋め込まれ、SVG画像はbase64でエンコードされ、マークアップに直接埋め込まれます。 base64形式でエンコードされたデータは、エンコードされていない同等のものよりも約1.37倍大きいことに注意してください。
- HTTP / 2-プッシュCSS
シナリオごとに、次のコマンドでテストを開始しました。
sitespeed.io -d 1 -m 1 -n 25 -c cable -b chrome –v https://jeremywagner.me
このコマンドの機能を知りたい場合は、ドキュメントを参照してください 。 要するに、彼女は次の条件でhttps://jeremywagner.meで私のサイトのホームページをチェックします 。
- ページ上のリンクはスキャンされず、指定されたページのみがテストされます。
ページは25回テストされます。 - 信号往復時間の28ミリ秒に対応するネットワークプロファイルが使用され、着信速度は5000 kbit / s、発信速度は1000 kbit / sです。
- テストはGoogle Chromeを使用して開始されます。
各テストについて、3つのインジケータが収集され、表示されました。
最初の塗装時間
これは、ページがブラウザに表示され始めた時点です。 ページが高速でロードされているように見えるようにするには、この数値をできるだけ小さくする必要があります。
DOMContentLoaded時間
これは、HTMLドキュメントが完全にロードおよび解析されたときです。 同期されたJavaScriptコードはパーサーをブロックし、この数値を増やします。<script>
タグでasync
属性を使用すると、パーサーのブロックを防ぐことができます。
- ページ読み込み時間
これは、ページとそのリソースを完全にロードするのに必要な時間です。
テストパラメータを決定したら、結果を見てみましょう。
試験結果
上記の6つのシナリオに従ってテストを実施し、結果のグラフを作成しました。 各スクリプトがページ開始時間にどのように影響するかを見てみましょう。
最初に、スケジュールの構成方法について少し説明します。 青のグラフの一部は、平均時間に対応しています。 オレンジ色の部分は90%のレベルです。 灰色の部分は最大時間を示しています。
次に、表示されるものに進みましょう。 最も遅いシナリオは、HTTP / 2およびHTTP / 1を改善せずにサポートするWebサイトです。 CSSにサーバープッシュを使用すると、このテクノロジーを使用しない場合よりも平均で約8%高速になり、HTTP / 1サーバーにCSSを埋め込むよりも約5%高速になります。
ただし、考えられるすべてのリソースをプッシュすると、画像が少し変わります。ページを表示するのにかかる時間が少し長くなります。 HTTP / 1サーバーでは、可能なすべてのものが埋め込まれているため、パフォーマンスがわずかに低下します。
結論は明らかです。サーバープッシュを使用すると、埋め込みを使用したHTTP / 1よりもわずかに優れた結果を達成できます。 ただし、多くのリソースをプッシュまたは埋め込むと、リターンが減少します。
初めてサイトにアクセスする人にとって、サーバープッシュまたは組み込みのリソースを使用することは、改善しないよりも優れていることに注意してください。 また、テストはリソースの少ないWebサイトで実行されるため、このテストケースがサイトで達成可能なものを完全に反映していない可能性があることにも言及する価値があります。
DOMContentLoaded時間に対する各スクリプトの効果に移りましょう。
ここでの傾向は、1つの顕著な逸脱を除いて、前のチャートで見たものと大差ありません:HTTP / 1にすべての可能なリソースを埋め込む例は、かなり低いDOMContentLoaded時間を与えます。 これはおそらく、埋め込みによりロードに必要なリソースの量が減るため、パーサーが中断することなく作業を続行できるためです。
最後に、各シナリオでのページの読み込み時間がどのように変化するかを見てみましょう。
私たちが発見した以前のチャートの傾向は通常ここに保存されます。 CSSのみをプッシュすると、ページ全体のロードで最高のパフォーマンスが得られることがわかりました。 あまりにも多くのリソースをプッシュすると、場合によってはWebサーバーが少し「遅くなる」ことがありますが、何もプッシュしないよりはましです。 埋め込みと比較して、サーバープッシュは結果をもたらしました。
サーバープッシュの注意
サーバープッシュは、サイトを非効率的に運用するための万能薬ではありません。 良い結果を得るには、このテクノロジーを賢く使用する必要があります。 そして、ここにいくつかの重要なポイントがあります。
プッシュできるリソースが多すぎる
上記のシナリオの1つでは、多くのリソースをプッシュしますが、それらはすべて合計データのごく一部を占めています。 ブラウザーはHTMLだけでなく、並行してロードされる他のすべてのリソースをロードする必要があるため、多数の非常に大きなリソースをプッシュすると、ページの表示がすぐに遅延する可能性があります。 プッシュするものを選択することが最善です。 スタイルシート(これまでのところそれほど大規模ではありません)は、最初に選択するのに適しています。 次に、他にプッシュする意味があるものを評価します。
現在のページに関係のないリソースをプッシュできます。
特に訪問者の分析がある場合、これは必ずしも悪いことではありません。 良い例は、登録プロセスの進行中に次のページのリソースをプッシュする複数ページの登録フォームです。 ただし、同意しましょう。訪問者がまだ閲覧していないページのリソースを強制する必要があるかどうかわからない場合は、しないでください。 一部のユーザーは、限られた量のトラフィックで関税を使用する可能性があり、戦略によって実際の費用がかかる場合があります。
HTTP / 2サーバーを正しく構成する
一部のサーバーには、サーバープッシュに関連する多くの構成オプションがあります。 そのmod_http2
、Apacheのmod_http2
は、リソースプッシュを設定するためのオプションがいくつかあります。 H2PushPriority
パラメーターは特に興味深いものですが、私のサーバーの場合はデフォルトで残しました。 一部の実験では、パフォーマンスがさらに向上します。 各Webサーバーには、実験に使用できるスイッチと設定のセットがすべて揃っているので、マニュアルを注意深く読んで、利用可能なものを見つけてください。
プッシュされたリソースはキャッシュせずに残すことができます
, Server Push - , . , . mod_http2
Apache, , H2PushDiarySize
, H2O , cookie .
H2O-, - -, cookie. , , , CSS-Tricks . , RST_STREAM
, , . .
HTTP/2? , Server Push.
- , . : , - . CSS . , , . , , .
Server Push , H2O, , cookies. , . , , .
Server Push , :
- Server Push , Hypertext Transfer Protocol Version 2 (HTTP/2), Internet Engineering Task Force
- Modernizing Our Progressive Enhancement Delivery , Scott Jehl, Filament Group
- Innovating with HTTP 2.0 Server Push , Ilya Grigorik
. Yoav Weiss , as
( , ), . , preload
– , Server Push.
HTTP/2 Server Push. «- » .