Upgrade to Pro — share decks privately, control downloads, hide ads and more …

103 Early Hints

103 Early Hints

Tatsuki Sugiura

December 13, 2024
Tweet

More Decks by Tatsuki Sugiura

Other Decks in Programming

Transcript

  1. 自己紹介 - Tatsuki Sugiura
 • 現在 Repro で Booster という

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

  2. HTTP Status code 1xx 知ってますか?
 
 • HTTP のマイナーステータスコード 1xx


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

  3. Status code 1xx について
 
 • 実は HTTP Protocol は

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

  4. 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)

  5. 今回の主題: 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 が妥当

  6. 具体的にどういう応答?
 • Link: ヘッダで preload, preconnect などの対象を返す
 • CSP とも一緒に使える


    103 Early Hint Link: </image.png>; rel=preload; as=image 103 Early Hint Content-Security-Policy: style-src: self; Link: </style.css>; rel=preload; as=style 200 OK Content-Type: text/html <!doctype 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: </style.css>;rel=preload;as=style,</script.js>;rel=preload;as =script < < HTTP/2 103 Early Hints < Link: </LCPimage.jpg>;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
  7. モデルケース: 伝統的な Web application の処理
 DBデータ フェッチ
 /書き込み
 /各種処理
 リクエスト


    time
 HTML組 み立て
 HTML返却
 cssリクエスト
 cssリクエスト
 JavaScript実行
 サブリソースの返却
 jsリクエスト
 画像リクエスト
 この間クライアントはヒマ
 HTMLパース
 jsリクエスト
 画像リクエスト

  8. TTFBは伝統的なWebアプリでは意外と長い
 • Page Speed Insight のしきい値で Good が 800ms
 •

    TTFB までの間、クライアントができることはないのでただ待っている
 • この間に先にリソースがロードできたらいいよね!
 というのが Early Hints の動機
 • (ただし、SPA や API ベースのサイトの場合はこのモデルに合致しない)

  9. 103 Early Hints を使うと
 DBデータ フェッチ
 / 書き込み
 /各種処理
 リクエスト


    time
 HTML組 み立て
 メインリソース HTML返却
 cssリクエスト
 cssリクエスト
 JavaScript実行
 サブリソースの返却
 jsリクエスト
 画像リクエスト
 HTMLパース
 画像リクエスト
 jsリクエスト
 Early
 Hints
 Hint を元に
 prefetch

  10. 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: <https://cdn.shopify.com>; rel=preconnect, <https://cdn.shopify.com>; crossorigin; rel=preconnect < < HTTP/2 200 < date: Fri, 13 Dec 2024 04:02:00 GMT < content-type: text/html; charset=utf-8 (略)
  11. 課題
 • 誰が 103 Early Hints を返すのか?
 ◦ どのサーバが返すのか
 •

    何を Hint として返すのか?
 ◦ そもそも Hint にする値で妥当なものはなにか?
 ◦ どうやって妥当なものを見つけるか?

  12. 誰が 103 Early Hints を返すのが適切?
 
 
 App server
 Load


    balancer
 WAF
 CDN
 Edge Cache
 Internal Reverse Proxy
 昨今の web アプリのインフラはとても多段に中継されている

  13. App server が 103 を返す
 App server
 • メリット: 返すべきリソースの知識は一番持っている、はず?


    • 課題: 経路のサーバは全部 103 Early Hint が通るのか?
 • 課題: 利用しているアプリケーションフレームワークに Early Hints を返す機能があるのか?
 • 課題: Plain HTTP/1.1 で返すのか? SSL + HTTP/2 を使うのか?

  14. 対応状況 (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 を レスポンスとして扱って しまう
  15. 対応状況 (Clients) - 補足
 • Firefox は対応していて動くが、devtool 上は特に表示されない
 ◦ Prelaod

    されるリソースは Network tab には普通に出るが、Initiator は as の値になってい る?
 • Chrome の場合、devtool に別セクションとして表示される
 https://developer.chrome.com/docs/web-platform/early-hints
  16. 対応状況 (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 ❌
  17. • だいたいサーバの Response/Request に専用のメソッドが作られている
 
 
 
 
 • Ruby

    rack は env['rack.early_hints'] を経由してサーバの機能を呼ぶ
 API
 const earlyHintsLink = '</styles.css>; rel=preload; as=style'; response.writeEarlyHints({ 'link': earlyHintsLink }); NodeJS http
 request.send_early_hints("link" => "</style.css>; rel=preload; as=style,</script.js>; rel=preload") Rails ActionDispatch::Request#send_early_hints

  18. 対応状況 (HTTP Server)
 対応状況 補足 nginx ✅ 別モジュールで対応 Apache mod_http2

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

  19. 対応状況 (HTTP Proxy)
 対応状況 補足 nginx (proxy) ✅ Apache mod_proxy

    ❌ 1xx は捨ててしまう。最終応答しか中継しない h2o haproxy ✅ Charles ❌‼ エラーになる mitmproxy ❌ 中継せずに捨ててしまう
  20. Edge cache が 103 を返す
 • メリット: 中継されずに消える心配は無い
 • メリット:

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

  21. 対応状況 (CDN)
 オリジン103の中継 透過キャッシュ 補足 AWS CloudFront ❌ ❌ 単に無視される

    Cloudflare ✅ Fastly ⚠ VCLで対応 Akamai Google Cloud CDN すみません、調べきれなかった

  22. Edge cache で 103 に変換する方式
 • App server は通常の 200

    に preload 用の Link ヘッダをつけて返す
 • それをキャッシュし、次からは 103 レスポンスとして返却する
 https://blog.cloudflare.com/early-hints/
  23. 誰が 103 Early Hints を返すのか? (再)
 
 • 現時点ではオリジンが返すようにする場合、インフラの検討が必要
 ◦

    Rails などは対応しているが、実際に使うのは大変かもしれない
 ◦ 中間の中継サーバは意外と対応していない
 • エッジキャッシュサーバが返すことができる場合は非常に効率が良い
 ◦ 一方、エッジキャッシュの機能に依存する
 ◦ 古いデータを返してしまって、無駄な読み込みが発生する可能性は残る

  24. 何を 103 Early Hints として返すのか?
 • Rails のような asset precompile

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

  25. アイデア: 実ユーザのデータを使う
 • LCP や CSS の情報を別経路で収集する
 • 集約し、妥当なロード対象を決める
 •

    そのデータを元にエッジキャッシュ(もしく は経路の誰か)が 103 を返却する
 103
 LCP, CSS…

  26. 103 Early Hints は夢があるけど課題も多い
 • リソースロードの最適化のためには結構効果はありそう
 • 素直に使えるなら、使いたいところだけど
 • 実際に適用しようとすると、インフラも対象選択も壁が多い


    • 現在 Repro Booster で簡易実現できないか模索中です!
 ◦ 103 status 自体は使わず、実質同じ事はできないか?
 ▪ navigate で遷移する直前に js で cache fill を試みる
 ◦ ロード対象決定のデータ集約を組み込んだサービスを実現したい
 ◦ 何某か知見をお持ちの方、詳しい方ぜひ色々教えてください!!