Slide 1

Slide 1 text

103 Early Hints
 2024-12-13 Tatsuki Sugiura 
 
 


Slide 2

Slide 2 text

自己紹介 - Tatsuki Sugiura
 ● 現在 Repro で Booster という Web サイト高速化 ツールの開発をしています
 ● 個人では RoR を使った開発や和暦gem とか
 ● ESR の 「ハッカーになろう」から OSS活動をはじめる
 ● 過去に OSDN や スラド (/.j) の開発・運営
 ● お茶が大好きです!


Slide 3

Slide 3 text

HTTP Status code 1xx 知ってますか?
 
 ● HTTP のマイナーステータスコード 1xx
 ● アンケート: どれでもいいので 1xx のコード知ってますか?
 a. 聞いたことなかった
 b. なんか聞いた記憶がある・見かけたことがある
 c. 仕様は読んだことがある
 d. 実際に使った・もしくはサーバから受け取ったことがある
 


Slide 4

Slide 4 text

Status code 1xx について
 
 ● 実は HTTP Protocol は リクエスト:レスポンス = 1:1 ではない!
 ● 1xx を使うと1リクエストに対して複数(1+)のレスポンスが返せる
 ● ブラウザ/クライアントはほぼ対応している
 ○ とはいえ、エラーにはならないだけで無視する場合も多い
 ● サーバの対応はまちまち
 ● リバースプロキシの対応はかなり微妙


Slide 5

Slide 5 text

HTTP Status code 1xx の歴史
 ● 実は HTTP/1.0 (1996) から 1xx に関する記述はある
 ○ この頃は “1xx は有効なレスポンスではないが実験的に有用かも” と言うだけ
 ● HTTP/1.1 (1997) で “100 Continue” と “101 Switching Protocols” が規定
 ○ サーバは HTTP/1.0 クライアントに 1xx を送ってはいけない (MUST NOT)
 ○ クライアントは 1xx を無視しても良い(MAY)が、エラーにならず受け入れ必須(MUST)
 ○ プロキシは 1xx を(例外を除き)クライアントに転送しなくてはならない (MUST)
 ○ この時点ではほぼ使われなかったが、WebSocket (2011) で 101 の利用が進む
 ● HTTP/2 (2015)
 ○ 101 が非サポートに (多重化を扱えないため)
 ● 103 Early Hints 登場 (2017, RFC 8297)


Slide 6

Slide 6 text

今回の主題: 103 Early Hints
 ● RFC 8297 (2017 Experimental)
 ● HTTP/2 server push では実現できなかった、クライアント主導のサブリ ソースリソースの先読みを実現する
 ○ Server push では既にキャッシュ済みのデータを送ってしまったりする可能性があり、サ ブリソースの先読みには使いにくかった
 ● その後 HTML Living Standard でも規定
 ○ 互換性の理由から、通常 Early hints は HTTP/2 以上のクライアントに配信される
 ● HTTP/2 h2c や HTTP/1.1 でも一応使えるが、現実的には HTTPS が妥当


Slide 7

Slide 7 text

具体的にどういう応答?
 ● Link: ヘッダで preload, preconnect などの対象を返す
 ● CSP とも一緒に使える
 103 Early Hint Link: ; rel=preload; as=image 103 Early Hint Content-Security-Policy: style-src: self; Link: ; rel=preload; as=style 200 OK Content-Type: text/html ... sugi@tempest:~% curl -v http://127.0.0.1:8000/ * Trying 127.0.0.1:8000... * Connected to 127.0.0.1 (127.0.0.1) port 8000 * using HTTP/1.x > GET / HTTP/1.1 > Host: 127.0.0.1:8000 > User-Agent: curl/8.11.0 > Accept: */* > * Request completely sent off < HTTP/2 103 Early Hints < Link: ;rel=preload;as=style,;rel=preload;as =script < < HTTP/2 103 Early Hints < Link: ;rel=preload;as=image < < HTTP/2 200 OK < Connection: close < Content-Type: text/plain < Hello, World! time is 2024-12-12 10:13:13 +0900 * shutting down connection #0

Slide 8

Slide 8 text

モデルケース: 伝統的な Web application の処理
 DBデータ フェッチ
 /書き込み
 /各種処理
 リクエスト
 time
 HTML組 み立て
 HTML返却
 cssリクエスト
 cssリクエスト
 JavaScript実行
 サブリソースの返却
 jsリクエスト
 画像リクエスト
 この間クライアントはヒマ
 HTMLパース
 jsリクエスト
 画像リクエスト


Slide 9

Slide 9 text

TTFBは伝統的なWebアプリでは意外と長い
 ● Page Speed Insight のしきい値で Good が 800ms
 ● TTFB までの間、クライアントができることはないのでただ待っている
 ● この間に先にリソースがロードできたらいいよね!
 というのが Early Hints の動機
 ● (ただし、SPA や API ベースのサイトの場合はこのモデルに合致しない)


Slide 10

Slide 10 text

103 Early Hints を使うと
 DBデータ フェッチ
 / 書き込み
 /各種処理
 リクエスト
 time
 HTML組 み立て
 メインリソース HTML返却
 cssリクエスト
 cssリクエスト
 JavaScript実行
 サブリソースの返却
 jsリクエスト
 画像リクエスト
 HTMLパース
 画像リクエスト
 jsリクエスト
 Early
 Hints
 Hint を元に
 prefetch


Slide 11

Slide 11 text

Cloudflare はこれで 30% サイトを高速化しているらしい
 https://blog.cloudflare.com/early-hints/

Slide 12

Slide 12 text

103 Real world example 実際に使われている例
 ● Shopify
 ● Shopify のサイトならどこで も試せる
 ● ただ、あまり効果的な指定 ができているわけではなさそ う
 sugi@tempest:~% curl -o/dev/null -v https://www.mollyjogger.com/ (略) > GET / HTTP/2 > Host: www.mollyjogger.com > User-Agent: curl/8.11.0 > Accept: */* > * Request completely sent off < HTTP/2 103 < link: ; rel=preconnect, ; crossorigin; rel=preconnect < < HTTP/2 200 < date: Fri, 13 Dec 2024 04:02:00 GMT < content-type: text/html; charset=utf-8 (略)

Slide 13

Slide 13 text

いいじゃん
 使おうぜ!
 と、行きたいところなんですけど……


Slide 14

Slide 14 text

課題
 ● 誰が 103 Early Hints を返すのか?
 ○ どのサーバが返すのか
 ● 何を Hint として返すのか?
 ○ そもそも Hint にする値で妥当なものはなにか?
 ○ どうやって妥当なものを見つけるか?


Slide 15

Slide 15 text

誰が 103 Early Hints を返すのが適切?
 
 
 App server
 Load
 balancer
 WAF
 CDN
 Edge Cache
 Internal Reverse Proxy
 昨今の web アプリのインフラはとても多段に中継されている


Slide 16

Slide 16 text

App server が 103 を返す
 App server
 ● メリット: 返すべきリソースの知識は一番持っている、はず?
 ● 課題: 経路のサーバは全部 103 Early Hint が通るのか?
 ● 課題: 利用しているアプリケーションフレームワークに Early Hints を返す機能があるのか?
 ● 課題: Plain HTTP/1.1 で返すのか? SSL + HTTP/2 を使うのか?


Slide 17

Slide 17 text

対応状況 (Clients)
 対応状況 補足 Chrome ✅ >= 103 HTTP/1.1 での 103 応答は無視する模様 ? Safari ✅ >= 17 Firefox ✅ >= 120 HTTP/1.1 でも可。最初の EarlyHints しか利用しない wget ✅ 単に無視する curl ✅ 単に無視する JavaScript fetch ✅ 単に無視する Ruby net/http ✅ 単に無視する Python http ✅ (⚠) HTTP/1.1 の場合は、最初の 1xx を レスポンスとして扱って しまう

Slide 18

Slide 18 text

対応状況 (Clients) - 補足
 ● Firefox は対応していて動くが、devtool 上は特に表示されない
 ○ Prelaod されるリソースは Network tab には普通に出るが、Initiator は as の値になってい る?
 ● Chrome の場合、devtool に別セクションとして表示される
 https://developer.chrome.com/docs/web-platform/early-hints

Slide 19

Slide 19 text

対応状況 (App Server / Framework / Lang)
 103出力対応状況 補足 Ruby on Rails ✅ rails >= 5.2.0 –early-hints オプションが必要。puma/falcon/unicorn で対応 NodeJS http ✅ >= 18.11.0 http ライブラリのみ。 express などは対応していない PHP (Apache/fpm) ❌ PHP (FrankenPHP) ✅ Python WSGI ❌ Python ASGI ✅ Extension で対応 Java JakartaEE ✅ >= 5.0.0-M1 Java Tomcat ✅ >= 11.0.0-M23 Rust hyper ❌

Slide 20

Slide 20 text

● だいたいサーバの Response/Request に専用のメソッドが作られている
 
 
 
 
 ● Ruby rack は env['rack.early_hints'] を経由してサーバの機能を呼ぶ
 API
 const earlyHintsLink = '; rel=preload; as=style'; response.writeEarlyHints({ 'link': earlyHintsLink }); NodeJS http
 request.send_early_hints("link" => "; rel=preload; as=style,; rel=preload") Rails ActionDispatch::Request#send_early_hints


Slide 21

Slide 21 text

対応状況 (HTTP Server)
 対応状況 補足 nginx ✅ 別モジュールで対応 Apache mod_http2 ⚠ Server push のバリアントとして対応自体はし ているのだが、as が設定できない? h2o ✅ ● 単に設定に書くだけだと、固定値が設定できるだけなので現実的には使えない
 ● 動的にするにはモジュールを書くとか、ビルトインスクリプトを使うなどが必要
 ● あまり現実的な用途はなさそう


Slide 22

Slide 22 text

対応状況 (HTTP Proxy)
 対応状況 補足 nginx (proxy) ✅ Apache mod_proxy ❌ 1xx は捨ててしまう。最終応答しか中継しない h2o haproxy ✅ Charles ❌‼ エラーになる mitmproxy ❌ 中継せずに捨ててしまう

Slide 23

Slide 23 text

Edge cache が 103 を返す
 ● メリット: 中継されずに消える心配は無い
 ● メリット: オリジンに向かうリクエストと同時に 103 をクライアン トに返せば一番早い
 ● 課題: Preload の対象を何らかの手段で渡す必要がある
 ● 課題: もしくはキャッシュや収集した値などを元に決める
 CDN
 Edge Cache


Slide 24

Slide 24 text

対応状況 (CDN)
 オリジン103の中継 透過キャッシュ 補足 AWS CloudFront ❌ ❌ 単に無視される Cloudflare ✅ Fastly ⚠ VCLで対応 Akamai Google Cloud CDN すみません、調べきれなかった


Slide 25

Slide 25 text

Edge cache で 103 に変換する方式
 ● App server は通常の 200 に preload 用の Link ヘッダをつけて返す
 ● それをキャッシュし、次からは 103 レスポンスとして返却する
 https://blog.cloudflare.com/early-hints/

Slide 26

Slide 26 text

誰が 103 Early Hints を返すのか? (再)
 
 ● 現時点ではオリジンが返すようにする場合、インフラの検討が必要
 ○ Rails などは対応しているが、実際に使うのは大変かもしれない
 ○ 中間の中継サーバは意外と対応していない
 ● エッジキャッシュサーバが返すことができる場合は非常に効率が良い
 ○ 一方、エッジキャッシュの機能に依存する
 ○ 古いデータを返してしまって、無駄な読み込みが発生する可能性は残る


Slide 27

Slide 27 text

何を 103 Early Hints として返すのか?
 ● Rails のような asset precompile の仕組みを持っている場合、必須のリ ソースは決定できる
 ● ページごとの LCP 画像も送りたいが、アプリケーションサーバが判断する のは困難なケースも多い
 ○ また、アプリケーションサーバが対象を決定するためには、内部の処理が終わってからで ないと分からないケースもある。その場合 103 を出すには遅すぎる。
 ● クライアントの画面サイズに依存する、js でクリティカルな CSS を差し込 んでいるなど、実際にレンダリングしてみないとわからないケースも多い


Slide 28

Slide 28 text

何を返すのか? 
 ● 絶対使う最低限のアセットだけ指定する
 ● ページをレンダリングしまくるバッチを運用して情報を取り出す
 ● 機械学習などで、HTML/CSS/JS から想定されるリソースを推定できるよう にるする
 ● 実ユーザのデータを収集して使う 


Slide 29

Slide 29 text

アイデア: 実ユーザのデータを使う
 ● LCP や CSS の情報を別経路で収集する
 ● 集約し、妥当なロード対象を決める
 ● そのデータを元にエッジキャッシュ(もしく は経路の誰か)が 103 を返却する
 103
 LCP, CSS…


Slide 30

Slide 30 text

103 Early Hints は夢があるけど課題も多い
 ● リソースロードの最適化のためには結構効果はありそう
 ● 素直に使えるなら、使いたいところだけど
 ● 実際に適用しようとすると、インフラも対象選択も壁が多い
 ● 現在 Repro Booster で簡易実現できないか模索中です!
 ○ 103 status 自体は使わず、実質同じ事はできないか?
 ■ navigate で遷移する直前に js で cache fill を試みる
 ○ ロード対象決定のデータ集約を組み込んだサービスを実現したい
 ○ 何某か知見をお持ちの方、詳しい方ぜひ色々教えてください!!