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

103 Early Hints

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

103 Early Hints

Avatar for Tatsuki Sugiura

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 を試みる
 ◦ ロード対象決定のデータ集約を組み込んだサービスを実現したい
 ◦ 何某か知見をお持ちの方、詳しい方ぜひ色々教えてください!!