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

>100 Views

June 05, 26

スライド概要

シェア

またはPlayer版

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

ダウンロード

関連スライド

各ページのテキスト
1.

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

2.

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

3.

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

4.

HTML生成中の待ち時間を埋めるのが、 HTTP 103 Early Hints 4

5.

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

6.

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

7.

1xx のステータスコードの特徴 暫定レスポンスで、レスポンスボディを持たない レスポンスが2回返る ブラウザ Request 1xx 暫定レスポンス(ボディなし) Server 処理は継続 最終レスポンス(2xx など) 7

8.

レスポンスが2回返っている HTML リクエストに 103 と 200 が並んでいる様子 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.

× Hono × Cloudflare PHP / Hono:バックエンドの処理を担当 Nginx / Cloudflare CDN:103 の生成・送出 18

19.

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

20.

アプリは103を書かない 生成はインフラ / CDN 層任せ PHP + Nginx / サイドカー アプリは普通に200を返すだけ。103はサイドカー/Nginxが送 出 Hono + Cloudflare アプリは103を発行しない。CloudflareがエッジでLinkから103 を生成 20

21.

103 を生成・転送する2つの部品 バックエンドの処理を待たず、サイドカーが res.writeEarlyHints() で 103 を生成。 それをフロントの Nginx が early_hints on; でクライアント へ転送する。 location / { early_hints on; # ★ upstream の 103 をクライアントへ転送 proxy_pass http://hints:3000; proxy_http_version 1.1; proxy_set_header Host $host; } 21

22.
[beta]
Early Hints が届く様子
curl -v --http1.1 -H 'Accept: text/html' http://localhost:8888/demo
< HTTP/1.1 103 Early Hints
← ① 先に届く(サイドカーが送出 / Nginx
< Link: </css/demo.css>; rel=preload; as=style
< HTTP/1.1 200 OK
< x-powered-by: PHP/8.4.21

← ② 約200ms後(PHPの本文)
← 200 は PHP-FPM 由来

ブラウザでの見え方

22

23.

色々設定が大変だと感じた方 FrankenPHPなら headers_send(103); を差し込めば OK! https://frankenphp.dev/ 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 の送信 なし あり( headers_send(103) ) preload 対象アセット — demo.css / inter-bold.woff2 / demo.js ダッシュボード 34

35.

計測結果 ① サーバー処理が長く、描画開始までの待ち時間が長い画面 指標 なし あり 差 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 ⚠️ 35

36.

改善事項 LCP が 800ms → 384ms(−52%)に短縮 リソース読み込み遅延が 260ms → 7ms にほぼ消滅 LCP 計測時点でヒーロー画像はダウンロード完了済み 36

37.

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

38.

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

39.

計測結果② 全体指標 指標 なし あり 差 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 39

40.

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

41.

ネットワークタブ② 103なし HTML後に CSS, JS が直列で開始 103対応 HTML生成中に CSS, JS, font, 画像を並行DL ※ ②(クリティカルリソース固定)のネットワークタブ実測イメージ(数値は別計測の中央値) 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