ブログ | NGINX

NGINX 1.13.9 による HTTP/2 サーバー プッシュの導入

NGINX-F5 水平黒タイプ RGB の一部
オーウェン・ギャレット サムネイル
オーウェン・ギャレット
2018年2月20日公開

NGINX Plus R15には、HTTP/2 サーバー プッシュのサポートも含まれています。

2018 年 2 月 20 日にリリースされたNGINX 1.13.9に、HTTP/2 サーバー プッシュのサポートが含まれることをお知らせします。 NGINX Plus ユーザーの場合、HTTP/2 サーバー プッシュ サポートは、2018 年 4 月に予定されているNGINX Plus R15リリースに含まれる予定です。

HTTP/2 仕様で定義されているサーバー プッシュを使用すると、サーバーは、クライアントがすぐにリソースを要求する可能性があることを予測して、事前にリソースをリモート クライアントにプッシュできます。 そうすることで、ページ読み込み操作における RTT (ラウンドトリップ時間、つまりリクエストと応答に必要な時間) の数を 1 RTT 以上削減でき、ユーザーへの応答が速くなります。

サーバー プッシュを使用すると、Web ページのレンダリングに必要なスタイル シート、画像、その他のリソースをクライアントに提供できます。 必要なリソースのみをプッシュするように注意する必要があります。クライアントがすでにキャッシュしている可能性のあるリソースはプッシュしないでください。

このブログ投稿では、次のことについて説明します。

HTTP/2 サーバープッシュの設定

ページの読み込みと同時にリソースをプッシュするには、次のようにhttp2_pushディレクティブを使用します。

server { # サーバーで HTTP/2 が有効になっていることを確認します listen 443 ssl http2 ; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # クライアントが demo.html を要求するたびに、 # /style.css、/image1.jpg、/image2.jpg もプッシュします location = /demo.html { http2_push /style.css; http2_push /image1.jpg; http2_push /image2.jpg; } }

HTTP/2 サーバープッシュの検証

次の 2 つの方法のいずれかを使用して、サーバー プッシュが有効になっているかどうかを簡単に確認できます。

  • ウェブブラウザの開発者ツール
  • nghttpなどのコマンドラインHTTP/2クライアント

開発者ツールで検証する (Google Chrome)

ここでは、Google Chrome を例に、Web ブラウザの開発者ツールを使用してサーバー プッシュが有効になっているかどうかを確認する方法を説明します。 図では、Chrome の開発者ツールの[ネットワーク]タブの[イニシエーター]列に、 /demo.htmlのリクエストの一部としていくつかのリソースがクライアントにプッシュされたことが示されています。

イニシエーター列は、リソースの送信にサーバープッシュが使用されたことを示します。

コマンドラインクライアント ( nghttp ) による検証

Web ブラウザ ツールに加えて、 nghttp2.orgプロジェクトのnghttpコマンドライン クライアントを使用して、サーバー プッシュが有効になっていることを確認できます。 nghttpコマンドライン クライアントはGitHubからダウンロードできます。また、適切なオペレーティング システム パッケージがある場合はそれをインストールすることもできます。 Ubuntu の場合は、 nghttp2-clientパッケージを使用します。

出力では、アスタリスク (*) はサーバーによってプッシュされたリソースを示します。

$ nghttp -ans https://example.com/demo.html id responseEnd requestStart process code size request path 13 +84.25ms +136us 84.11ms 200 492 /demo.html 2 +84.33ms * +84.09ms 246us 200 266 /style.css 4 +261.94ms * +84.12ms 177.83ms 200 40K /image2.jpg 6 +685.95ms * +84.12ms 601.82ms 200 173K /image1.jpg

クライアントへのリソースの自動プッシュ

多くの場合、NGINX 構成ファイルにプッシュしたいリソースをリストすることは不便であり、不可能ですらあります。 このため、NGINX はLinkプリロード ヘッダーをインターセプトし、これらのヘッダーで識別されるリソースをプッシュするという規則もサポートしています。 プリロードを有効にするには、設定にhttp2_push_preloadディレクティブを含めます。

server { # サーバーで HTTP/2 が有効になっていることを確認します listen 443 ssl http2 ; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # Link ヘッダーを傍受し、要求されたプッシュを開始します location = /myapp { proxy_pass http://upstream; http2_push_preload on; } }

たとえば、NGINX がプロキシ (HTTP、FastCGI、またはその他のトラフィック タイプ用) として動作している場合、アップストリーム サーバーは次のようなLinkヘッダーを応答に追加できます。

リンク: </style.css>; as=style; rel=preload

NGINX はこのヘッダーをインターセプトし、 /style.cssのサーバー プッシュを開始します。 リンクヘッダー内のパスは絶対パスである必要があります。./style.cssのような相対パスはサポートされていません。 パスにはオプションでクエリ文字列を含めることができます。

複数のオブジェクトをプッシュするには、複数のLinkヘッダーを指定するか、さらに良い方法として、すべてのオブジェクトをコンマ区切りのリストに含めることができます。

リンク: </style.css>; as=style; rel=preload, </favicon.ico>; as=image; rel=preload

NGINX がプリロードされたリソースをプッシュしないようにするには、ヘッダーにnopushパラメータを追加します。

# リソースはプッシュされませんLink: </nginx.png>; as=image; rel=preload; nopush

http2_push_preloadが有効になっている場合は、NGINX 構成で応答ヘッダーを設定することで、プリロード サーバー プッシュを開始することもできます。

add_header リンク "</style.css>; as=style; rel=preload";

クライアントにリソースを選択的にプッシュする

HTTP/2 仕様では、リソースをプッシュするかどうかを決定するという課題には対処していません。 明らかに、クライアントがリソースを必要とする可能性が高く、すでにキャッシュされている可能性が低いことがわかっている場合は、リソースのみをクライアントにプッシュするのが最善です。

考えられるアプローチの 1 つは、クライアントがサイトに初めてアクセスしたときにのみリソースをプッシュすることです。 たとえば、セッション クッキーの存在をテストし、条件付きでLinkヘッダーを設定することで、セッション クッキーが存在しない場合にのみリソースがプリロードされるようにすることができます。

クライアントが適切に動作し、後続のリクエストに Cookie を含めると仮定すると、次の構成では、NGINX はブラウザ セッションごとに 1 回だけリソースをクライアントにプッシュします。

server {
listen 443 ssl http2 default_server;

ssl_certificate ssl/certificate.pem;
ssl_certificate_key ssl/key.pem;

root /var/www/html;
http2_push_preload on;

location = /demo.html {
add_header Set-Cookie "session=1";
add_header Link $resources;
}
}

map $http_cookie $resources {
"~*session=1" "";
default "</style.css>; as=style; rel=preload, </image1.jpg>; as=image; rel=preload, </image2.jpg>; as=image; rel=preload";
}

HTTP/2 サーバープッシュの効果の測定

サーバー プッシュの効果を測定するために、別のスタイルシート/style.cssを参照する簡単なテスト ページ/demo.html を作成しました。 スタイルシートはさらに 2 つの画像を参照します。 3 つの異なる構成を使用してページの読み込み時間をテストしました。

  • シーケンシャルGET (最適化なし) – ブラウザは必要なリソースを発見するとそれをロードします。
  • プリロードヒント– プリロードヒント(リンクヘッダー)は、ブラウザに依存関係をロードするように指示するための最初の応答に含まれていました。
  • サーバープッシュ(HTTP/2のみ) – 依存関係が事前にブラウザにプッシュされました
サーバープッシュによる HTTP/2 の影響を測定するために 3 つの構成がテストされました

HTTP、HTTPS、または HTTP/2 を使用して、各構成のテストを複数回実行しました。 最初の 2 つの構成は 3 つのプロトコルすべてに適用され、サーバー プッシュは HTTP/2 にのみ適用されます。

動作は Chrome 開発者ツールを使用して測定されました。 各構成の最も一般的な動作が評価され、平均化され、その時間がリンクの RTT ( ping を使用して測定) と相関付けられ、各方法の機械的な効果を示しました。

各構成で多くのラウンドトリップが発生したことを示すテスト結果

いくつかの基本的な観察

  • DOM がロードされると、新しい接続が開始され、 demo.htmlページが取得されます。 スタイルシートは、 CSS リソースを取得するときです。
  • HTTP 経由の接続を確立するには 1 RTT かかります。HTTPS および HTTP/2 の場合は 2 RTT かかります。
  • HTML および CSS リソースのペイロードは最大転送単位 (MTU) サイズよりも小さいため、 GET操作は約 1 RTT で完了します。

結果の解釈: プリロードのヒント

  • プリロードヒントは HTML リソース内で直接参照され、HTML リソースがすぐに配信されるため、CSS リソースへの影響は最小限です。 ブラウザは HTML ページが配信されるとすぐに CSS リクエストを開始します。
  • プリロードヒントには、CSS リソースで宣言されているリソース (ここでは 2 つの画像) のダウンロードをすぐに開始する効果があります。 プリロードヒントを使用すると、ダウンロードを 1 RTT 速く開始できます。
  • プリロードヒントによる実質的な節約は 1 RTT 以上になる可能性があります。 リソースを並行してダウンロードする場合、ブラウザは 1 つ以上の追加接続を開く必要があります。 パフォーマンスは、遅いリクエスト (大きな応答) が最初にスケジュールされるか、新しい接続が開かれる間に遅延されるかによって異なります。 この予測不可能なリクエスト順序により、HTTP および HTTP/2 では 1-RTT の高速化が実現され、HTTPS では 2-RTT の高速化が実現されます。

結果の解釈: サーバープッシュ

  • サーバー プッシュにより、プリロード ヒントの時間はさらに 1 RTT 改善されました。 プッシュ「応答」は最初のリクエストへの応答と同時に開始されましたが、プリロードヒント応答では 1 RTT の遅延が発生しました (最初のリクエストへの応答の 0.5 RTT とプリロードGETリクエストの 0.5 RTT)。

テストノート

  • 構成ごとに複数回のテスト実行がありました。 各実行は、ブラウザ キャッシュが空で、NGINX サーバーへのキープアライブ接続が確立されていない状態で開始されました。 NGINX ディレクティブkeepalive_timeoutおよびhttp2_idle_timeout を使用して、キープアライブ接続をすばやく閉じました。
  • 現時点では、既知の問題のためか、フォント リソースを Chrome にプッシュすることはできないようです。 Chrome は、フォント リソースがすでにプッシュされている場合でも、明示的にフォント リソースを要求します。
  • 各テストの前にブラウザのキャッシュを明示的にクリアするように注意し、すべてのコンテンツは期限切れのキャッシュ制御ヘッダーとともに提供されました。
  • Chrome はプリロードされたリソースをキャッシュします。 これらのキャッシュされたリソースは、キャッシュを無効にしても常に無視されるわけではなく、明示的な「ブラウザ キャッシュのクリア」操作によって常にクリアされるわけでもありません。 (Chrome でキャッシュを無効にする方法の 1 つは、開発者ツールの[ネットワーク]タブで[キャッシュを無効にする]チェックボックスをオンにすることです。 ブラウザのキャッシュをクリアするには、開発者ツールを開いた状態でブラウザの更新ボタンを右クリックし、 「キャッシュを空にしてハードリロード」を選択します。
  • コンテンツをプリロードしようとすると、Chrome はキャッシュされたコピーの再検証に失敗し、リソースを通常どおりダウンロードします。 これらの試みは測定にはカウントされませんでした。
  • Chrome は、以前に承認された自己署名証明書を使用するすべての新しい SSL 接続に、不要な 2 RTT の遅延を追加します。 この 2 RTT の遅延を回避するために、CA 署名付きの Let’s Encrypt 証明書を使用してテストを実施しました。
  • DNS 遅延は、ローカルの/etc/hostsファイルを編集することで解決されました。
  • これらの単純なテストでは、クライアントがリソースのキャッシュされたコピーをすでに持っている場合のサーバー プッシュの効果を測定しようとはしませんでした。 テストでは、各テストの前にすべてのキャッシュがクリアされ、ほとんどのプッシュはキャンセルするには速すぎました。

結論

このテストは、プリロードヒントとサーバープッシュの仕組みを強調するために意図的に単純化されました。 サーバー プッシュは、単純な状況ではプリロード ヒントよりも 1 RTT の改善をもたらし、最適化されていないシーケンシャルGETリクエストや依存リソースの検出と比べても大きな改善をもたらします。

より現実的なユースケースには、複数の依存リソース、複数のソース、すでにキャッシュされているリソースやすぐに必要ではないリソースをプッシュすることで帯域幅が無駄になる可能性など、さらに多くの変数があります。 ブラウザの不一致もパフォーマンスに影響します。 この簡単なテストによって、走行距離は確実に変わります。

たとえば、Chrome チームは、サーバー プッシュを展開するタイミングに関する詳細な推奨事項を公開しており、より複雑なサイトで測定を行って、最適化なし、プリロード ヒント、HTTP/2 経由のサーバー プッシュの効果を比較しています。 彼らの「HTTP/2 プッシュに関する経験則」レポートは、本番環境で HTTP/2 サーバー プッシュを導入することを検討している人にとって一読の価値があります。

実用的な結論としては、事前に必要なリソースを特定できる場合、上流サーバーにプリロードヒントを送信させることに実際のメリットがあるということです。 これらのリソースをプッシュすることによる追加の利点は小さいながらも測定可能ですが、帯域幅の無駄や必要なリソースの遅延につながる可能性があります。 サーバープッシュ構成は慎重にテストおよび監視する必要があります。

付録: HTTP/2 プッシュはどのように機能しますか?

以下の情報は、Jake Archibald の非常に詳細なブログ投稿「HTTP/2 の推進は思ったより困難です」の調査に一部基づいています。

HTTP/2 サーバー プッシュは通常、クライアントがリソースを要求したときに、依存するリソースを事前に送信するのに使用されます。 たとえば、クライアントが Web ページを要求すると、サーバーは依存するスタイルシート、フォント、および画像をクライアントにプッシュすることがあります。

クライアントが HTTP/2 接続を行うと、サーバーは接続を介して 1 つ以上のサーバー プッシュ応答を開始することを選択できます。 これらのプッシュは、クライアントが明示的に要求していないリソースを送信します。

クライアントは、プッシュを拒否するか( RST_STREAMフレームを送信して)、または受け入れることができます。 クライアントは、プッシュされたコンテンツを、HTTP/2 接続に関連付けられたローカルの「プッシュ キャッシュ」に保存します。

その後、クライアントが確立された HTTP/2 接続を使用してリソースを要求すると、接続のプッシュ キャッシュで要求に対する完了した応答または転送中の応答がチェックされます。 リソースに対して新しい HTTP/2 リクエストを行うよりも、キャッシュされたリソースを優先して使用します。

プッシュされたリソースは、(a) 使用されるか、(b) HTTP/2 接続が閉じられるまで、接続ごとのプッシュ キャッシュに残ります。

  1. リソースが使用されている場合、クライアントはコピーを取得し、プッシュ キャッシュ内のエントリは削除されます。 リソースがキャッシュ可能な場合、クライアントはそのコピーを HTTP ページ キャッシュにキャッシュできます。
  2. 何らかの理由で HTTP/2 接続が閉じられると、ローカル プッシュ キャッシュは削除されます。

これにはいくつかの意味があります:

  • プッシュされたコンテンツの方が新しい場合でも、ブラウザの HTTP ページ キャッシュ内のコンテンツがプッシュ キャッシュ内のコンテンツよりも優先して使用されます。
  • HTTP/2 接続は、異なるページの読み込み間で共有できます。 あるページの読み込みの結果としてプッシュされたリソースは、別のページの読み込みで要求されたときに使用できます。
  • 資格情報付きのリクエストは、資格情報なしのリクエストとは異なる HTTP/2 接続を使用します。たとえば、クロスオリジン リクエスト (資格情報付き) でプッシュされたリソースは、ブラウザーがリソースに対して資格情報なしのリクエストを行った場合、見つからない可能性があります。

問題のより詳細なリストについては、Jake Archibald のブログ投稿「HTTP/2 のプッシュは思ったより難しい」で確認できます。

HTTP/2 サーバー プッシュは興味深い機能です。 HTTP/2 サーバーのプッシュ構成を徹底的にテストし、より予測可能なキャッシュ対応の動作が得られる場合には、プリロードヒントにフォールバックできるように準備しておいてください。


「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"