103 Early Hintsで始める、サーバー・フロントの協調最適化

2.4K Views

June 05, 26

スライド概要

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

103 Early Hintsで始める、 サーバー・フロントの協調最適化 1

2.

Webページの表示には通信が必須 サーバーがHTMLを生成している間 描画やリソースの取得は止まる 2

3.

Early Hintsが解決すること HTML生成中に CSS / JS / font などのリソース取得のヒントを送信 結果、画面描画が早くなる! HTMLのレンダリング中に、リソースヒントが並行して始まっている様子 3

4.

HTML生成中の待ち時間をうまく使うのが HTTP 103 Early Hints 4

5.

このセッションのゴール バックエンドで どう実装するのか ブラウザが どう解釈・活用するのか どのUX指標が どれだけ改善するのか 5

6.

プロフィール 発表者 ma@me 所属 最近の業務 品質改善・不具合対応 6

7.

1xx のステータスコードの特徴 1回のリクエストを受けて、次のようなレスポンスを返す 1. Headerのみの、ヒントのレスポンス 2. Bodyを含む、最終レスポンス ブラウザ Request 1xx 暫定レスポンス(ボディなし) Server 処理は継続 最終レスポンス(2xx など) 7

8.

レスポンスが2回返っている HTML リクエストに 103 と 200 が並んでいる様子 1つがヒントの中間レスポンス、1つが最終レスポンス 8

9.

Early Hintsのケース 通常の200ステータスのケース 9

10.

103 応答ヘッダーの中身 約165バイトのヘッダー Link: 〜の部分が先行取得の指示 server: 103の送出箇所。このケースではFrankenPHPのCaddyから送出 10

11.

Early Hintsを含む 歴代の先行取得手法を紹介 11

12.

① HTTP/2 Server Push ブラウザで再現できないので、イメージ図 登場年代:2015 サーバーが要求されていないリソースをHTMLと同時に押し出す仕組み。 ブラウザのキャッシュ状況をサーバーが把握できず、不要なリソースまで送 ってしまうため廃止 12

13.

② <link rel="preload"> <link rel="preload" href="/style.css" as="style"> <link rel="preload" href="/app.js" as="script"> 登場年代:2016 指定したリソースを優先的に先行取得するようブラウザに指示する HTML内で完結するため、サーバー側の対応が不要。主要ブラウザで広く対 応済み HTMLとは直列の取得になる 13

14.

③ 103 Early Hints 登場年代:RFC 2017 / ブラウザ実装 2022 HTML生成中にリソースの先行取得を始めるため、HTMLとリソースの取得 が並列で進む。 14

15.

各手法と取得タイミングまとめ 手法 タイミング 主導権 HTTP/2 Server Push レスポンス時 サーバー <link rel="preload"> HTML到達後 ブラウザ 103 Early Hints HTML生成中 サーバー 15

16.

103は、本レスポンス到達前の空白時間を有効活用 ① <link rel="preload"> ― HTML到達後に発火 ③ 103 Early Hints ― 本レスポンス(200)到達前にヒント送信 16

17.

103 Early Hints に対してサーバー側が どう主導権を握り どう扱うのか サーバー構成での実装を通して紹介 17

18.

PHPでの実現例 × × PHP:バックエンドの処理を担当 Nginx:103 の転送・送出 Node.js:Nginxのサイドカー。103 を生成 18

19.

構成の全体像 Client フロント Nginx Request 内部Nginx + PHP サイドカー Request Request ② 103 Early Hints ③ 200 OK(本文) 19

20.

の仕事 res.writeEarlyHints() で 103 を生成して、Nginxへ転送 公式ドキュメント: https://nodejs.org/api/http.html#responsewriteearlyhintshintscallback 20

21.

103 を生成・転送する2つの部品 Nginx は受け取った103,200を適宜クライアントへ転送。 location / { early_hints on; # ★ upstream proxy_pass http://hints:3000; proxy_http_version 2; proxy_set_header Host $host; } の 103 をクライアントへ転送 21

22.

Early Hints が届く様子 < HTTP/2 103 Early Hints < Link: </css/demo.css>; rel=preload; as=style < HTTP/2 200 OK < x-powered-by: PHP/8.4.21 ブラウザでの見え方 22

23.

色々大変だな、と感じた方 PHPerなら FrankenPHP がおすすめ! headers_send(103); を差し込めばOK! バックエンドがJSなら Cloudflare CDN 23

24.

FrankenPHPって何?という方へ 過去のFrankenPHP関連の登壇スライド どこまで違う?!PHP実行環境パフォーマンス対決 - mod_php vs php-fpm vs Swoole vs FrankenPHP Node.jsに頼らずにFrankenPHPでリアルタイムWeb通信を実現する Laravel OctaneはFrankenPHPをどう高速化しているのか?ソースコードか ら読み解く、高速化の仕組み 24

25.

実装ができても、どの画面でも効くわけで はない ここからは Early Hints が効く画面を見極め る話 25

26.

103 が効く2つの画面構成 1. サーバー処理が長く、描画開始までの待ち時間が長い 2. クリティカルリソースが固定 26

27.

1. サーバー処理が長く 描画開始までの待ち時間が長い画面 サーバー側の処理で、本レスポンスまでに時間がかかる(目 安:200ms〜) DB クエリ 外部 API 呼び出し など 27

28.

イメージ コンテンツ例 EC のカート / 注文確認 ダッシュボード 検索結果 / 一覧画面 認証後のマイページ 28

29.

2. クリティカルリソースが固定の画面 このページなら必ずこれを読む、リソースがバチっと決まって いる画面 29

30.

LP イメージ コンテンツ例 LCP になるヒーロー画像 Web フォント ファーストビューの CSS エントリ JS 30

31.

パフォーマンス検証 1. サーバー処理が長く、描画開始までの待ち時間が長い 2. クリティカルリソースが固定 実際にどれくらい速くなるのか、計測結果を見ていく。 31

32.

FCP / LCP とは? FCP(First Contentful Paint) 最初のテキストや画像が表示された瞬間 LCP(Largest Contentful Paint) 主要コンテンツ(最大要素)が表示された瞬間 = ユーザーが体感する表示完了 32

33.

計測対象のサイト 同じ HTML を返す2つのエンドポイントを用意。 共通条件 HTML を返すまでの遅延:200ms(両エンドポイント共通) = 200 OK までの TTFB:約 200ms 各アセット / API の遅延:各 200ms ※ TTFB は 103 のヒントが届いた時間になり、描画は始まらな いので参考数値扱い。 33

34.

計測対象のサイト 項目 /demo-no-early /demo-early 103 Early Hints の送信 なし preload 対象アセット — headers_send(103) ) demo.css / inter-bold.woff2 / demo.js あり( ダッシュボード 34

35.

計測結果 指標 なし あり 差 13 ms 9 ms −4 ms FCP 300 ms 308 ms +8 ms LCP 300 ms 308 ms +8 ms 全リソース完了 499 ms 228 ms −271 ms(−54%) TTFB ⚠️ サブリソース別(取得完了) リソース なし あり 差 CSS 441 ms 227 ms −214 ms JS 440 ms 228 ms −212 ms font 682 ms 228 ms −454 ms 画像 695 ms 267 ms −428 ms 35

36.

改善事項 全リソース完了が 499ms → 228ms(−54%)に短縮 CSS → font の発見カスケードが解消し、並列ロードに LCP は元々高速(300ms 前後)で横ばい 36

37.

ネットワークタブ 103なし HTML 完了後に CSS, JS が直列で開始。 font はさらに後(発見カスケード) 103あり HTML 生成中に CSS, JS, font, 画像を並行ダウンロード ※ ①(ダッシュボード)のネットワークタブ実測イメージ(数値は別計測) 37

38.

計測対象 ② クリティカルリソースが固定の画面 38

39.

計測結果 指標 なし あり 差 218 ms 10 ms −208 ms FCP 504 ms 308 ms −196 ms LCP 800 ms 384 ms −416 ms(−52%) 全リソース完了 693 ms 269 ms −424 ms TTFB ⚠️ 39

40.

改善事項 FCP が 504ms → 308ms(−39%)に短縮 LCP が 800ms → 384ms(−52%)に短縮 LCP 計測時点でヒーロー画像はダウンロード完了済み 40

41.

ネットワークタブ 103なし 103対応 ※ ②(クリティカルリソース固定)のネットワークタブ実測イメージ(数値は別計測の中央値) 41

42.

サーバー処理が長いほど効果あり HTML をクライアントへ返すまでの時間が長いページほど、 Early Hints の効果は大きい。 SQL クエリ 外部 API 呼び出し テンプレート描画 etc. 42

43.

103で速度を解決! …とはいかないパターンも どんな構成にも効く銀の弾丸ではない。 効果が出るパターン、出ないパターンを確認。 43

44.

フロント構成との相性問題 ✅ 効果あり MPA / SSR ❌ 効果薄 SPA(CSR中心) 44

45.

なぜ SPA では効果が薄いのか サーバーが返すHTMLが薄く、待ち時間がほとんど生まれない ため、Early Hints の効果が薄い。 メインとなるHTMLのレスポンスタイム <!doctype html> <html lang="ja"> <head>...</head> <body> <div id="root"></div> <script type="module" src="/app.js"></script> </body> </html> ソース全体像 45

46.

❌ とりあえずearly hints は 外したヒントは、かえって遅くなる 103は名前の通り、あくまでヒント。 そのページで使わないリソースを指すと逆効果 Link: </app.css>; rel=preload Link: </unused.css>; rel=preload app.cssは全てのページで利用 unused.cssはindexページでしか利用しないのに、全部のページ で読み込んでいる、など 46

47.

適切に導入すれば ユーザー体験アップ ⤴️ ご清聴ありがとうございました 47