Web Performance

サイトが送るべきなのは、より小さいリクエストではなく、より少ないリクエスト

小さなファイルも無料ではありません。モダンな HTTP はリクエストのオーバーヘッドを小さくしましたが、無関係にしたわけではありません。

The Wux Webtools Team The Wux Webtools Team 14 分読 AI支援、人的レビュー済み
A stylized browser waterfall showing many small requests simplified into fewer intentional requests.
目次
  1. 「すべてのファイルを小さくすればよい」という心地よい神話
  2. リクエストは単なるバイトではない
  3. 「でも HTTP/2 が解決したのでは」は、ほとんど違う
  4. 真実は waterfall にある
  5. 小さなファイルは今でも重要。ただし等しく重要ではない
  6. 多数の小さなファイルに潜むコスト
  7. 1. 遅い発見
  8. 2. ヘッダーのオーバーヘッド
  9. 3. main thread の中断
  10. 4. キャッシュの複雑さ
  11. bundling は戻ってきた。ただし判断が必要
  12. third-party request は特に疑ってかかるべき
  13. リクエスト削減の実践チェックリスト
  14. 削除する
  15. 考えてまとめる
  16. 遅らせる
  17. 適切にキャッシュする
  18. 再測定する
  19. 良い状態とは何か

「すべてのファイルを小さくすればよい」という心地よい神話

長年、Web パフォーマンスの助言は単純に聞こえました。すべてを圧縮し、すべてを minify し、すべてのアセットを小さくする、というものです。

その助言は、今でもおおむね正しいものです。40 KB のスクリプトは、たいてい 400 KB のスクリプトより優れています。最適化された画像は、生の書き出しより優れています。Brotli、AVIF、CSS minification、tree shaking、font subsetting は、どれも重要です。

しかし多くの本番サイトでは、より大きな問題はもはや、ひとつの巨大なファイルではありません。ページが使えるように感じられる前に、ブラウザが要求しなければならないものの数です。

ファイルサイズだけを見れば整っているように見えるページでも、120 件のリクエストを送っているために遅くなることがあります。CSS の断片、JavaScript チャンク、third-party タグ、フォントファイル、トラッキングピクセル、アイコンスプライト、JSON エンドポイント、preload、analytics beacon、キャッシュの再検証。ひとつひとつは「小さい」かもしれません。しかし集まると、長く壊れやすい waterfall を作ります。

実用的な原則はこうです。個々のアセットが十分に圧縮されているなら、すべてのファイルから数 KB を削るより、リクエスト数を減らすほうがユーザー体験を改善することがよくあります。

リクエストは単なるバイトではない

ネットワークリクエストは、データ転送だけではありません。一連の処理です。

ブラウザはリソースを発見し、いつ取得するかを決め、他のリソースとの兼ね合いでスケジュールし、ヘッダーを送り、サーバーを待ち、ヘッダーを受け取り、レスポンスを解析し、多くの場合は展開し、それから何か有用なことをしなければなりません。

その「有用なこと」は高くつく場合があります。JavaScript ファイルは parse、compile、execute される必要があります。CSS ファイルは rendering をブロックすることがあります。フォントファイルは読めるテキストを遅らせたり、layout shift を引き起こしたりします。画像は Largest Contentful Paint 要素に影響するかもしれません。third-party script は独自の依存関係の連鎖を持ち込むことがあります。

だからこそ、ファイルが小さくてもリクエスト数は依然として重要です。3 KB のスクリプトは、rendering をブロックし、遅れて到着し、悪いタイミングで main thread 上で実行されるなら、30 KB の画像より悪いことがあります。

Lighthouse レポートを読んでいて、ばらばらの警告が何十も出ることに責められているように感じるなら、個々のスコアではなく、まず request waterfall を見てください。慌てずに Lighthouse レポートを読む方法については実践的な手順がありますが、短く言えば、first render をブロックしているものと、主要コンテンツを遅らせているものを見つけることです。

「でも HTTP/2 が解決したのでは」は、ほとんど違う

HTTP/2 と HTTP/3 は、リクエストの経済性を変えました。multiplexing、header compression、より良い接続動作を導入しました。平たく言えば、ブラウザは、より少ない接続で複数のリクエストを送るのがはるかに得意になりました。

それは確かな改善でした。同時に、接続数制限を避けるためだけの極端な CSS スプライトや巨大な結合バンドルといった古い習慣も終わらせました。

しかし HTTP/2 は、リクエストを無料にしたわけではありません。

multiplexing は、多くのリソースが接続を共有する場合に役立ちますが、それでもブラウザはそれらに優先順位をつけなければなりません。サーバーは依然として応答しなければなりません。クライアントは依然として各レスポンスを処理しなければなりません。輻輳、packet loss、TLS negotiation、DNS lookup、cache miss、main-thread pressure は残ります。

HTTP/3 は、特に connection migration や transport layer の head-of-line blocking まわりで、一部の transport 動作を改善します。しかし、リソースの発見、スケジューリング、ダウンロード、解析、実行のコストを取り除くわけではありません。

したがって現代の目標は「すべてをひとつの巨大ファイルに bundle する」ことではありません。「critical なリクエストを減らし、残るリクエストを意図的にする」ことです。

真実は waterfall にある

パフォーマンス問題は、ひとつのメトリクスの中で名乗りを上げることはあまりありません。形として現れます。

ブラウザの network panel を開き、最初の数秒を見てください。問いかけるべきことは次のとおりです。

  • メインコンテンツが表示される前に、いくつのリクエストが開始されているか?
  • どのリクエストが rendering をブロックしているか?
  • 重要なリソースの発見が遅れていないか?
  • third-party script が first-party の CSS、フォント、画像と競合していないか?
  • 多くのファイルがキャッシュから直接配信されるのではなく、304 レスポンスを返していないか?
  • アイコン、フォント、UI 断片が、ページに必要な数以上のファイルに分割されていないか?

速いページは、たいてい初期 waterfall が退屈です。少数の critical なリソースが早く到着します。critical でないリソースは待ちます。third-party script は遅延され、制限され、または削除されています。ブラウザは、ページを paint する前に 20 個もの優先順位をさばくことを強いられません。

遅いページには、不安定な waterfall がよく見られます。小さなファイルが多く、origin が多く、発見が遅いのです。

小さなファイルは今でも重要。ただし等しく重要ではない

これは圧縮や最適化に反対する議論ではありません。調整を無視してバイトだけを最適化することへの反論です。

小さなファイルが最も重要になるのは、そのリソースが大きい、render-blocking である、またはメインコンテンツの経路に含まれる場合です。たとえば次のようなものです。

  • hero 画像は、適切なサイズとエンコードにすべきです。
  • render-blocking CSS は軽量にすべきです。
  • 最初の interaction に必要な JavaScript は最小限にすべきです。
  • フォントは subset し、圧縮し、実際に使う weight に限定すべきです。

フォントはよくある例です。チームは、フォントファイルが 24 KB か 31 KB かにこだわる一方で、6 つの weight、2 つの style、複数の family を配信していることがあります。より良い改善は、ひとつのファイルから 7 KB を削ることではありません。送るフォントファイルを減らすことです。typography がパフォーマンス改善の一部なら、web fonts are still one of the easiest wins on most sites が役に立つでしょう。

画像も同じ構図です。AVIF や WebP は意味のあるバイト削減につながりますが、above the fold に装飾画像を 10 枚送るのは依然として悪い計画です。より良い format を選ぶことは大切です。しかし、それぞれの画像をそもそもリクエストする必要があるのかも問うべきです。format の判断には、when AVIF beats WebP and when it does not のガイドが、この request-count の取り組みの良い補助になります。

多数の小さなファイルに潜むコスト

小さなリクエストが多いと、総転送バイト数だけを見ていては現れない問題が生まれがちです。

1. 遅い発見

ブラウザは、発見していないものをリクエストできません。CSS ファイルはフォントを参照できます。スクリプトは別のスクリプトを import できます。コンポーネントは hydration 後に JSON をリクエストできます。それぞれの依存関係が、連鎖にもう一段のステップを作ります。

連鎖が深いほど、重要な処理の開始は遅くなります。

2. ヘッダーのオーバーヘッド

すべてのリクエストとレスポンスにはヘッダーが含まれます。header compression は、特に HTTP/2 と HTTP/3 では役立ちますが、オーバーヘッドをなくすわけではありません。Cookie はこれを大きく悪化させることがあります。サイトがすべてのリクエストに大きな Cookie を送っているなら、小さなアセットは実際にはそれほど小さくありません。

これが、静的アセットは cookie-free な path や domain に置くのがよい場合が多い理由のひとつであり、cache header に注意を払うべき理由でもあります。本番環境でヘッダーの挙動がおかしい場合は、推測するよりも debugging redirects and HTTP headers のほうがたいてい速く進みます。

3. main thread の中断

JavaScript チャンクが多いと、parse と実行が繰り返し発生することがあります。各チャンクが小さくても、ブラウザはコードを評価するために何度も立ち止まるかもしれません。これは Interaction to Next Paint を悪化させ、ページをぎこちなく感じさせることがあります。

ユーザーは、各ファイルが小さかったかどうかを気にしません。メニューをタップするのに 600 ミリ秒かかったことを気にします。

4. キャッシュの複雑さ

アセットの分割は、慎重に行えばキャッシュを改善できます。安定した vendor bundle と、変化する app bundle に分けるのは良い分割になりえます。

しかし過度な chunking は裏目に出ることがあります。ファイルが増えるほど、cache lookup が増え、revalidation の機会が増え、version coordination が増え、変更不要だったリソースを誤って invalidate する経路も増えます。

bundling は戻ってきた。ただし判断が必要

Web パフォーマンスの最初の時代は、ブラウザに厳しい接続数制限があったため bundling を好みました。その後 HTTP/2 が登場し、多くのチームは aggressive code splitting へ大きく振れました。その一部は有用でした。一部は迷信になりました。

妥当な中間点は route-aware bundling です。

典型的な marketing site や content site では、次のようにします。

  • initial rendering に必要な CSS だけを inline する、または load する。
  • global JavaScript を小さく保つ。
  • 小さな module を個別の network request に分割しない。
  • すぐには不要な interactive feature を遅らせる。
  • コストに見合わない third-party script を削除する。

アプリケーションでは、次のようにします。

  • すべての component ごとではなく、route や主要機能ごとに分割する。
  • 共有依存関係を安定させ、cacheable に保つ。
  • 近いうちに確実に必要なリソースだけを preload する。
  • admin、dashboard、editor、experiment code を public page で load しない。
  • bundle size だけでなく interaction cost を測定する。

bundling は自動的に良いわけではありません。code splitting も自動的に良いわけではありません。有用な問いは、この分割が、次に意味のあるユーザー体験をより早く届ける助けになるか、です。

third-party request は特に疑ってかかるべき

first-party request は少なくとも自分たちで制御できます。third-party request は多くの場合、見た目より遅く、予測しにくく、高くつきます。

ひとつの tag manager が、analytics、ads、heatmaps、chat widgets、A/B testing、consent tools、personalization scripts を起動することがあります。各 vendor がさらにリクエストを持ち込むかもしれません。早い段階で実行されるものもあります。main thread をブロックするものもあります。自分たちのリリースプロセスなしに変わるものもあります。

最善の third-party 最適化は削除です。次善は遅延です。

third-party script を追加する前に、次のことを確認してください。

  • これはユーザーがページを見る前に load する必要があるか?
  • すべてのページで load する必要があるか?
  • consent、interaction、または idle time の後に load できないか?
  • 社内の owner は誰か?
  • その performance cost に見合うことを証明する metric は何か?

ここでパフォーマンスは governance になります。誰かが「いいえ」と言える必要があります。

リクエスト削減の実践チェックリスト

まず最も重要なページから始めます。homepage、pricing page、product page、checkout、signup、または主要な landing page です。それから waterfall を順に確認します。

削除する

  • 未使用の JavaScript と CSS を削除する。
  • 古い experiment、放置された pixel、重複する analytics を削除する。
  • 未使用の font weight と icon library を削る。
  • 適切な場合は、装飾画像を CSS に置き換える。

考えてまとめる

  • 常に一緒に load される小さな JavaScript module を bundle する。
  • 同じ render path をブロックする小さな CSS ファイルを merge する。
  • 保守性を損なわずにリクエストを減らせる場合は、繰り返し使う icon に SVG sprites や inline SVG を使う。

遅らせる

  • below-the-fold 画像を lazy-load する。
  • non-critical script を first paint 後、または user interaction 後まで遅らせる。
  • comments、embeds、maps、chat、video players は必要になったときだけ load する。

適切にキャッシュする

  • versioned static assets には long-lived caching を使う。
  • ほとんど変わらないファイルの不要な revalidation を避ける。
  • HTML は新鮮に保ち、hashed assets は cache に残せるようにする。

再測定する

変更のたびに、もう一度 waterfall を確認してください。目標は完璧なスコアではありません。critical request を減らし、有用な rendering を早め、main-thread disruption を減らすことです。

良い状態とは何か

健全なページは、必ずしも可能な限りリクエストが少ないページではありません。小さく、意図的な critical path を持つページです。

ブラウザは HTML、essential CSS、必要ならメインコンテンツ画像、navigation や above-the-fold interaction に必要な小さな script、そしてテキストを読めるようにするための最小限の font set を受け取ります。それ以外は順番を待ちます。

それが、単に最適化されたページと、速く感じるページの違いです。

ファイルを小さくすることは、今でも行う価値があります。しかしサイトがすでに十分に圧縮されているなら、次のパフォーマンス改善はたいてい、bundle からさらに 2 KB 削ることではありません。blocking request をひとつ減らすこと、font file をひとつ減らすこと、third-party script をひとつ減らすこと、dependency chain をひとつ減らすことです。

リクエストが少ないほど、ブラウザの仕事は単純になります。認めたくないこともありますが、単純であることは、速さにつながることが多いのです。

よくある質問

ひとつの大きな bundle は、多くの小さなファイルより良いですか?
自動的にそうとは限りません。ひとつの巨大な bundle は、特に初回 load であらゆるものを遅らせる可能性があります。多くの小さなファイルは、スケジューリングや実行のオーバーヘッドを生む可能性があります。より良いパターンは、常に一緒に必要なリソースを bundle し、route や主要機能ごとに分割することです。
HTTP/2 なら、リクエスト数はもう重要ではないのですか?
いいえ。HTTP/2 は multiplexing と header compression によって接続のオーバーヘッドの一部を減らしますが、各リクエストには依然として発見、優先順位付け、サーバー、キャッシュ、解析、実行のコストがあります。
critical CSS はすべて inline すべきですか?
本当に critical な CSS を少量 inline することは first render に役立つ場合がありますが、inline しすぎると HTML が重くなり、キャッシュしにくくなります。最小限に保ち、効果を測定してください。
リクエストを減らしやすい場所はどこですか?
フォントと third-party script は、多くの場合、最も早く効果が出る領域です。多くのサイトは、未使用の font weight、重複した analytics、古い pixel、chat widget、またはすぐに load する必要のない embed を配信しています。
ページのリクエスト数はいくつにすべきですか?
普遍的な目標値はありません。小さな content page では critical request はごく少なくすべきです。複雑な app ではもっと必要な場合があります。first render 前と主要な interaction path 前のリクエストを減らすことに集中してください。

参考文献&さらなる読み物

  1. MDN Web Docs: HTTP caching
  2. web.dev: Optimize Largest Contentful Paint
  3. RFC 9113: HTTP/2
  4. HTTP Archive Web Almanac: Page Weight
著者について
The Wux Webtools Team

最終更新:

読み続ける