web-dev-qa-db-ja.com

Webサーバーがページを送信するときに、必要なすべてのCSS、JS、および画像を要求されることなく送信しないのはなぜですか?

Webページに単一のCSSファイルと画像が含まれている場合、ブラウザとサーバーがこの従来の時間のかかるルートで時間を浪費するのはなぜですか。

  1. ブラウザは、Webページの最初のGET要求を送信し、サーバーの応答を待ちます。
  2. ブラウザはcssファイルに対して別のGET要求を送信し、サーバーの応答を待ちます。
  3. ブラウザは画像ファイルの別のGET要求を送信し、サーバーの応答を待ちます。

代わりに、この短くて直接的な時間節約のルートを使用できるのはいつですか?

  1. ブラウザはWebページのGETリクエストを送信します。
  2. Webサーバーは(index.htmlに続いてstyle.cssおよびimage.jpg)で応答します
45
Ahmed

簡単な答えは、「HTTPはそのために設計されていないため」です。

Tim Berners-Lee は、効率的で拡張可能なネットワークプロトコルを設計しませんでした。彼の1つの設計目標はシンプルさでした。 (大学の私のネットワーキングクラスの教授は、彼は仕事を専門家に任せるべきだと言っていました。)あなたが概説した問題は、HTTPプロトコルに関する多くの問題の1つにすぎません。元の形式:

  • プロトコルバージョンはなく、リソースのリクエストのみ
  • ヘッダーがありませんでした
  • 各リクエストには新しいTCP接続が必要でした
  • 圧縮はありませんでした

プロトコルは、これらの問題の多くに対処するために後で修正されました。

  • リクエストはバージョン管理され、リクエストはGET /foo.html HTTP/1.1のようになりました
  • 要求と応答の両方を含むメタ情報のヘッダーが追加されました
  • 接続はConnection: keep-aliveで再利用できました
  • 文書サイズが事前にわからない場合でも接続を再利用できるように、チャンク応答が導入されました。
  • Gzip圧縮が追加されました

この時点で、後方互換性を損なうことなく、HTTPは可能な限り使用されています。

ページとそのすべてのリソースをクライアントにプッシュすることを提案する最初の人ではありません。実際、Googleは SPDY と呼ばれるプロトコルを設計しました。

現在、ChromeとFirefoxの両方が、HTTPの代わりにSPDYをサポートするサーバーに使用できます。 SPDY Webサイトから、HTTPと比較した主な機能は次のとおりです。

  • SPDYを使用すると、クライアントとサーバーはリクエストヘッダーとレスポンスヘッダーを圧縮できます。これにより、複数のリクエストで同様のヘッダー(Cookieなど)が繰り返し送信される場合の帯域幅使用量が削減されます。
  • SPDYは、単一の接続で複数の同時多重化された要求を許可し、クライアントとサーバー間の往復を節約し、優先順位の低いリソースが優先順位の高い要求をブロックするのを防ぎます。
  • SPDYを使用すると、サーバーは、クライアントが要求するのを待たずに、クライアントが必要とするリソース(JavaScriptファイルやCSSファイルなど)をクライアントにアクティブにプッシュできるため、サーバーは未使用の帯域幅を効率的に使用できます。

SPDYを使用するWebサイトを、それをサポートするブラウザーに提供したい場合は、そうすることができます。たとえば、 Apacheにはmod_spdyがあります です。

SPDYは、サーバープッシュテクノロジを使用した HTTPバージョン2 の基礎になりました。

63

Webブラウザーは、サーバーからWebページ(HTML)をダウンロードするまで追加リソースを認識しません。サーバーには、それらのリソースへのリンクが含まれています。

サーバーが独自のHTMLを解析して、Webページの最初のリクエスト中にすべての追加リソースをWebブラウザーに送信しないのはなぜでしょうか。 これは、リソースが複数のサーバーに分散している可能性があり、Webブラウザーはそれらのリソースの一部が既にキャッシュされているか、サポートしていないため、これらすべてのリソースを必要としない可能性があるためです

Webブラウザはリソースのキャッシュを保持するため、ホストするサーバーから同じリソースを何度もダウンロードする必要はありません。すべてが同じjQueryライブラリを使用するWebサイト上の異なるページをナビゲートするときは、そのライブラリを毎回ダウンロードするのではなく、最初にダウンロードしたいだけです。

そのため、WebブラウザーはサーバーからWebページを取得すると、キャッシュにまだないリンクリソースを確認し、それらのリソースに対して追加のHTTP要求を行います。非常にシンプルで、非常に柔軟で拡張可能です。

通常、Webブラウザーは2つのHTTP要求を並行して作成できます。これはAJAXとは異なりません。これらは両方ともWebページをロードするための非同期メソッドです(非同期ファイルのロードと非同期コンテンツのロード)。 keep-aliveを使用すると、1つの接続を使用して複数のリクエストを作成でき、pipelining応答を待たずに複数の要求を行うことができます。ほとんどのオーバーヘッドは通常TCP接続のオープン/クローズに起因するため、これらの手法は両方とも非常に高速です。

keep-alive

pipelining

ウェブ履歴の一部...

Webページはプレーンテキストの電子メールとして始まり、コンピューターシステムはこのアイデアに基づいて設計され、やや自由なコミュニケーションプラットフォームを形成しました。当時のWebサーバーは依然として独自のものでした。後に、画像、スタイル、スクリプトなどの追加のMIMEタイプの形式で、より多くのレイヤーが「電子メール仕様」に追加されました。結局、MIMEはMulti-Purpose InternetMail拡張。遅かれ早かれ、本質的にマルチメディアメール通信、標準化されたWebサーバー、およびWebページができました。

HTTPでは、データは電子メールのようなメッセージのコンテキストで送信される必要がありますが、ほとんどの場合、データは実際には電子メールではありません。

このような技術が進化するにつれて、開発者が既存のソフトウェアを破壊することなく、新しい機能を段階的に組み込むことができるようにする必要があります。たとえば、新しいMIMEタイプが仕様に追加された場合(たとえばJPEG)、WebサーバーとWebブラウザーがそれを実装するのに時間がかかります。突然JPEGを仕様に強制してすべてのWebブラウザーに送信し始めるのではなく、Webブラウザーがサポートするリソースを要求できるようにするため、誰もが満足し、テクノロジーが前進し続けます。スクリーンリーダーはWebページ上のすべてのJPEGを必要としますか?おそらくない。お使いのデバイスがJavascriptをサポートしていない場合、Javascriptファイルの束を強制的にダウンロードする必要がありますか?おそらくない。 Googlebotは、サイトのインデックスを適切に作成するために、すべてのJavascriptファイルをダウンロードする必要がありますか?いや。

ソース:Node.jsのようなイベントベースのWebサーバーを開発しました。 Rapid Server と呼ばれます。

参照:

参考文献:

14
perry

彼らはそれらのリソースが何であるか知らないからです。 Webページに必要なアセットはHTMLにコード化されています。パーサーがそれらの資産が何であるかを決定した後にのみ、ユーザーエージェントはyを要求できます。

さらに、それらのアセットが判明したら、適切なヘッダー(つまりコンテンツタイプ)を提供できるように個別に提供する必要があるため、ユーザーエージェントはその処理方法を認識できます。

12
John Conde

あなたの例では、Webサーバーはクライアントがすでに持っているかどうかにかかわらず、CSSと画像をalways送信するため、帯域幅を大幅に浪費します(したがって接続を確立します) slower、遅延を減らすことで高速化する代わりに、おそらくあなたの意図でした)。 CSS、JavaScript、および画像ファイルは通常、まさにその理由で非常に長い有効期限で送信されることに注意してください(それらを変更する必要がある場合は、ファイル名を変更するだけで、新しいコピーが強制的に再びキャッシュされます)。

これで、「OK」と言って帯域幅の浪費を回避できますが、クライアントはすでにそのリソースの一部を持っていると示すことができるため、サーバーはそれを再送信しません 。何かのようなもの:

GET /index.html HTTP/1.1
Host: www.example.com
If-None-Match: "686897696a7c876b7e"
Connection: Keep-Alive

GET /style.css HTTP/1.1
Host: www.example.com
If-None-Match: "70b26618ce2c246c71"

GET /image.png HTTP/1.1
Host: www.example.com
If-None-Match: "16d5b7c2e50e571a46"

そして、変更されていないファイルのみを取得し、1つのTCP接続を介して送信します(永続的な接続を介したHTTPパイプライン化を使用)。そして何だと思う? alreadyの仕組みです( If-None-Match の代わりに If-Modified-Since を使用することもできます) 。


ただし、(元の要求のように)大量の帯域幅を無駄にしてレイテンシーを本当に削減したい場合は、Webサイトの設計時に標準のHTTP/1.1を使用して今日これを行うことができます。ほとんどの人がそれをしない理由は、それが価値があるとは思わないからです。

これを行うには、CSSまたはJavaScriptを個別のファイルに含める必要はありません。<style>および<script>タグを使用してメインHTMLファイルに含めることができます(おそらく手動で行う必要はありません) 、テンプレートエンジンでおそらく自動的に実行できます)。次のように、 data URI を使用してHTMLファイルに画像を含めることもできます。

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />

もちろん、base64エンコードは帯域幅の使用量をわずかに増加させますが、帯域幅の浪費を気にしないのであれば、それは問題になりません。

さて、本当に気にするなら、あなたは両方の世界の最高のものを得るためにあなたのウェブスクリプトを十分にスマートにすることさえできます:最初のリクエスト(ユーザーはクッキーを持っていません)で、たった1つのHTMLに埋め込まれたすべて(CSS、JavaScript、画像)を送信します上記のようにファイルを追加し、 link rel = "prefetch"タグを追加します ファイルの外部コピー用に、Cookieを追加します。ユーザーが既にCookieを持っている場合(たとえば、以前に訪問したことがある場合)、<img src="example.jpg"><link rel="stylesheet" type="text/css" href="style.css">などを含む通常のHTMLのみを送信します。

したがって、最初にアクセスすると、ブラウザーは1つのHTMLファイルのみを要求し、すべてを取得して表示します。その後、指定された外部CSS、JS、画像を(アイドル状態のときに)プリロードします。次回ユーザーがアクセスすると、ブラウザーは変更されたリソース(おそらく新しいHTML)のみを要求して取得します。

Webサイトで何百回クリックした場合でも、追加のCSS + JS + imagesデータは2回しか送信されません。提案されたソリューションが示唆した数百回よりもはるかに優れています。そして、(初回でも次回でもない)レイテンシーを増加させるラウンドトリップone以上を使用することはありません。

今、それがあまりにも多くの仕事のように聞こえ、 SPDY のような別のプロトコルを使いたくない場合、Apacheの mod_pagespeed のようなモジュールが既にあります。 Webページの1行を修正することなく、複数のCSS/JSファイルを1つにマージし、小さなCSSを自動インライン化してそれらを縮小し、オリジナルの読み込みを待つ間に小さなプレースホルダーのインライン画像を作成します。 。

8
Matija Nalis

HTTP2はSPDYに基づいており、まさにあなたが提案することを行います:

高レベルで、HTTP/2:

  • テキストではなくバイナリです
  • 順序付けられてブロックするのではなく、完全に多重化されている
  • したがって、並列処理に1つの接続を使用できます
  • ヘッダー圧縮を使用してオーバーヘッドを削減します
  • サーバーがクライアントキャッシュにプロアクティブに応答を「プッシュ」できるようにします

詳細は HTTP 2よくある質問 で入手できます

7
MaBu

これらが実際に必要とされるとは想定していないため

このプロトコルは、特定の種類のファイルまたはユーザーエージェントに対する特別な処理を定義しません。たとえば、HTMLファイルとPNG画像の違いはわかりません。 あなたが求めていることをするために、Webサーバーはファイルの種類を識別し、それを解析して、それが参照している他のファイルを見つけ出し、次に、他のどのファイルが実際に必要かを判断する必要がありますファイルを処理するつもりです。これには3つの大きな問題があります。

最初の問題は、サーバー側でファイルタイプを識別するための標準的で堅牢な方法はありませんです。 HTTPはContent-Typeメカニズムを介して管理しますが、それはサーバーを助けません。サーバーはそれ自体を理解する必要があります(一部はContent-Typeに何を入れるかを知るため)。ファイル名拡張子は広くサポートされていますが、悪意のある目的のために、脆弱で簡単にだまされます。ファイルシステムのメタデータは脆弱ではありませんが、ほとんどのシステムはメタデータをあまりサポートしていないため、サーバーも気にしません。コンテンツスニッフィング(一部のブラウザおよびUnix fileコマンドが実行しようとするもの)は、喜んで高価にしたい場合には堅牢になる可能性がありますが、堅牢なスニッフィングはサーバー側では実用的ではありません。十分に堅牢ではありません。

2番目の問題は、ファイルの解析は高価で、計算的に言えばです。これは、コンテンツを堅牢にスニッフィングする場合、さまざまな潜在的な方法でファイルを解析する必要があるという点で、最初のものと多少結びついていますが、ファイルタイプを特定した後にも適用されます参照が何であるかを把握します。これは、ブラウザのように一度に数個のファイルしか実行していない場合はそれほど悪くありませんが、Webサーバーは一度に数百または数千の要求を処理する必要があります。これは合計されますが、それが大きすぎると、実際には複数の要求よりも遅くなります。 Slashdotまたは同様のサイトからリンクを訪れたことがありますが、使用率が高いためにサーバーがひどく遅いことを確認するためだけに、この原則が実際に動作しているのを見ました。

3番目の問題は、サーバーには、ファイルをどうするかを知る方法がないです。ブラウザでは、HTMLで参照されているファイルが必要な場合がありますが、ファイルが実行されている正確なコンテキストによっては必要ない場合があります。それは十分に複雑ですが、Webには単なるブラウザ以上のものがあります:スパイダー、フィードアグリゲーター、ページスクレイピングマッシュアップの間参照されるファイルを必要としない多くの種類のユーザーエージェントがありますHTML:彼らはHTML自体だけを気にします。これらの他のファイルをこのようなユーザーエージェントに送信すると、帯域幅が浪費されるだけです。

要点は、サーバー側でこれらの依存関係を把握することは、それが価値があるよりも厄介なことです。その代わりに、クライアントに必要なものを理解させるのです。

3
The Spooniest