Slide 1

Slide 1 text

Introducing RFC9111 ⼩⼭健⼀郎 / Tailor Inc. 2025.11.14 YAPC::Fukuoka 2025

Slide 2

Slide 2 text

少し実⽤的で⼩さなOSSを書くのが趣味 ● GitHub: @k1LoW ● X: @k1LoW ● SWE at Tailor Inc. / Fukuoka.go ⼩⼭健⼀郎 ⾃⼰紹介 2 Ken'ichiro Oyama

Slide 3

Slide 3 text

https://careers.tailor.tech/ 全方位で採用中です! Your ERP, your way. 業務に最適な ERPを、思いのままに。 Tailorは、モノリシック(単一構造)な従来型ERPに代わる柔軟な モジュール型アーキテクチャを提供し、企業が自社に最適な業 務システムをAPIで自由に構成できる「コンポーザブルERP」を 実現します。 3

Slide 4

Slide 4 text

● 参考⽂献紹介 ● HTTPキャッシュ ○ RFC7234 ○ RFC9111 ● github.com/2manymws/rc ● RFC9111 ○ キャッシュの格納 ○ キャッシュの利⽤ Agenda 4

Slide 5

Slide 5 text

● https://gihyo.jp/book/2021/978-4-297-11925-6 ● RFC9111が公開される前の書籍。しかしドラフ ト時の内容に⾔及していて実質RFC9111 ● HTTPキャッシュの理解のためには正直この書籍 だけで⼗分   5 "Web配信の技術 ―HTTPキャッシュ‧リバースプロキシ‧CDNを活⽤する" 参考⽂献紹介 HTTPキャッシュ、リバースプロキシ、CDNなどWeb開発で ⼤切な「配信」の技術。 重要な技術ながら、現場では知⾒のあるエンジニアが少な く、なんとなくで運⽤されていたり、導⼊が遅れていたり します。 本書では、HTTPキャッシュの基礎から解説し、⼀冊でしっ かり配信が学べます。 速くて落ちないWebサイト∕Webサービス∕Web APIの実 現はもちろん。キャッシュ事故やセキュリティ上の問題を 防ぐのにも役⽴ちます。

Slide 6

Slide 6 text

HTTPキャッシュ 6

Slide 7

Slide 7 text

● 共有キャッシュ ● キャッシュの格納場所でいうと「1. クライアント側のキャッシュ(ローカルキャッシュ)」「2. 配信経路 上のキャッシュ」「3. オリジンのキャッシュ(ゲートウェイキャッシュ)」のうちゲートウェイキャッ シュ寄りのお話しです。 ○ 『Web配信の技術』. "2.5 キャッシュの格納場所による分類". p31 本発表のターゲットとなるHTTPキャッシュ HTTPキャッシュ 7

Slide 8

Slide 8 text

● RFC2616を廃⽌ ● 2014年6⽉ に公開 ● Proposed Standard HTTPキャッシュ 8 RFC7234 The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document defines HTTP caches and the associated header fields that control cache behavior or indicate cacheable response messages.

Slide 9

Slide 9 text

● RFC7234を廃⽌ ● 2022年6⽉ に公開 ● Internet Standard HTTPキャッシュ 9 RFC9111 The Hypertext Transfer Protocol (HTTP) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. This document defines HTTP caches and the associated header fields that control cache behavior or indicate cacheable response messages. This document obsoletes RFC 7234.

Slide 10

Slide 10 text

● NGINX ... 公式ドキュメントにRFC参照の明⽰なし ● Envoy ... RFC7234参照 ○ "HTTP Cache only caches responses with enough data to calculate freshness lifetime as per RFC7234" ○ https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/cache_filter ● Apache mod_cache ... RFC2616 参照 ○ "RFC 2616 compliant HTTP caching filter" "RFC 2616 allows for the cache to return stale data while the existing stale entry is refreshed" ○ https://httpd.apache.org/docs/current/mod/mod_cache.html ● Varnish ... RFC7234等を参照。RFC参照ページあり。ただし主要なキャッシュ動作についてRFC番号の明 ⽰なし ○ https://varnish-cache.org/rfc/ HTTPキャッシュ実装の参照RFCの現状 HTTPキャッシュ 10 プロキシサーバー(?)

Slide 11

Slide 11 text

● Fastly ... RFC9111 参照 ○ "Cache-Control response directives are defined by section 5.2.2 of RFC 9111" ○ https://www.fastly.com/documentation/guides/full-site-delivery/caching/about-cache-control- headers/ ● Cloudflare ... RFC7234 / RFC2616参照 ○ "aim to strictly adhere to RFC 7234" / "Expires header ... use Greenwich Mean Time (GMT) as stipulated in RFC 2616" ○ https://developers.cloudflare.com/cache/concepts/cache-control/ ● Akamai ... RFC7234 参照 ○ "enables honoring particular Cache-Control header directives from the origin and supports all official RFC 7234 directives except for no-transform" (enhancedRfcSupportオプション) ○ https://techdocs.akamai.com/property-mgr/reference/latest-caching HTTPキャッシュ実装の参照RFCの現状 HTTPキャッシュ 11 CDN

Slide 12

Slide 12 text

● RFC7234-RFC9111間で破壊的変更が少ない ○ 共有キャッシュにおける⼤きめの変更はmust-understandディレクティブの追加とWarningヘッ ダーの廃⽌くらい ● RFC9111は「定義をはっきりさせた」という印象。そして、そもそも定義の揺れは現実世界ではある程度 受け⼊れられていた ○ ディレクティブが競合する場合の⽅針の明確化や、publicおよびprivateディレクティブの明確化など ● そもそも世の中にはHTTPキャッシュについての独⾃実装や独⾃設定が溢れていてRFC完全準拠のメリット が⼩さい ○ 採⽤するプロキシサーバーやCDNの実装に応じて柔軟に対応している状況 「なぜ未だにRFC7234の参照が多いのか」についての私⾒ HTTPキャッシュ 12

Slide 13

Slide 13 text

github.com/2manymws/rc 13

Slide 14

Slide 14 text

● rc is a response cache middleware for cache. ○ https://github.com/2manymws/rc ● type rc.Cacher interface を満たす実装を⼊⼒にレスポンスキャッシュの HTTP middleware func(next http.Handler) http.Handler を作成するパッケージ github.com/2manymws/rc 14

Slide 15

Slide 15 text

● type rc.Handler interface も満たすとキャッシュのハンドリングを変更できる。 ○ デフォルトではRFC9111の仕様に沿って共有キャッシュを扱う 挙動になる ○ つまりデフォルトの実装 github.com/2manymws/rc/rfc9111 は type rc.Handler interface を満たした実装を持つ github.com/2manymws/rc   15 github.com/2manymws/rc/rfc9111

Slide 16

Slide 16 text

● ただし、リクエストディレクティブに対する挙動は意図的にほぼ未実装 ● github.com/2manymws/rc はオリジンのためのキャッシュ機構として使⽤することを想定しているため リクエストディレクティブ github.com/2manymws/rc 16 クライアントから max-age=0 を指定してリクエストを⾏ったとしても CDNが正しく解釈してオリジンに 検証する動作を筆者はみたことがありません。 (中略) ただ、リクエストヘッダの Cache-Control については、 名前に反して経路上のキャッシュでは考慮す る必要がない のです。 『Web配信の技術』. "Column リクエスト時にもCache-Controlは送信される". p82 (※強調は発表者)

Slide 17

Slide 17 text

● HTTP middleware handlers and related packages for Go ● 主に特殊な⽤途なHTTP middlewareやその周辺パッケージを提供している ○ rp ... rp is a reverse proxy package for multiple domains and multiple upstreams. ■ https://github.com/2manymws/rp ■ 複数ドメイン複数アップストリーム を実現するリバースプロキシ ○ rl ... rl is a rate limit middleware for multiple limit rules. ■ https://github.com/2manymws/rl ■ 複数ルールのレートリミット を実現するHTTP middleware ● Sliding Window Counterパターン ○ mm ... mm is a middleware-middleware for multiple rules. ■ https://github.com/2manymws/mm ■ 動的なHTTP middleware適⽤ルール を実現するHTTP middleware github.com/2manymws org github.com/2manymws/rc 17

Slide 18

Slide 18 text

● 世の中にはRFC7234参照の実装が多い。 しかし ● 個⼈的にその理由は「RFC7234-RFC9111間で破壊的変更が少ない」からであると考えており、逆にいうと RFC9111参照でも受け⼊れられるはず ● RFC9111はRFC7234よりも仕様がはっきりしているため、 むしろ実装側視点で⾒ると迷わない ● ⾝も蓋もない事実としては RFC7234はRFC9111によって廃⽌されている ○ RFCとしての将来性は明らか なぜRFC9111を選択したのか github.com/2manymws/rc 18

Slide 19

Slide 19 text

RFC9111 19

Slide 20

Slide 20 text

● HTTPキャッシュを構成する要素は⼤きく2つ ○ キャッシュを格納する仕組み ○ キャッシュを利⽤する仕組み ● この2つの要素をRFCに沿って実装していく HTTPキャッシュを構成する要素 RFC9111 20

Slide 21

Slide 21 text

● type rc.Handler interface のうち ○ Storable() が「キャッシュの格納」を実装 ○ Handle() が「キャッシュの利⽤」を実装 (再掲)github.com/2manymws/rc RFC91111 21

Slide 22

Slide 22 text

キャッシュの格納 22

Slide 23

Slide 23 text

● 「どのレスポンスをキャッシュとして格納して良いのか?」 ● RFC9111の "3. Storing Responses in Caches" に記載がある ○ https://www.rfc-editor.org/rfc/rfc9111#section-3 23 キャッシュの格納

Slide 24

Slide 24 text

RFC9111 - キャッシュの格納 24 キャッシュを格納する際のチャート

Slide 25

Slide 25 text

● そもそも理解可能なメソッドなのか。ステータスコー ドが >= 200 かどうか。 ● Cache-Control: no-store といった 格納不可かど うか ● Cache-Control: public といった 明⽰的なキャッ シュ許可ディレクティブがあるかどうか RFC9111 - キャッシュの格納 25 必須条件(格納禁⽌条件)チェック  

Slide 26

Slide 26 text

● RFC7234では Cache-Control: no-store はキャッシュ格納不可 ● RFC9111では Cache-Control: no-store であっても、 must-understand ディレクティブが存在し、 かつ、理解可能なステータスコードであれば no-store を無視してキャッシュすることが可能 ● 新しいキャッシュ可能なステータスコードや、特別なキャッシング動作を持つ既存のステータスコードが 導⼊された際に、古いキャッシュ機構が予期せぬキャッシュ格納を⾏うのを防ぎつつ、新しいキャッシュ 機構がキャッシュ格納できるようにする⽬的 ○ 新ステータスコードを理解不可な場合 ... no-store ということで、キャッシュしない ○ 新ステータスコードを理解可能な場合 ... no-store を無視して、後続のキャッシュ判定フローに乗 せて判定する must-understand
 RFC9111 - キャッシュの格納 26

Slide 27

Slide 27 text

RFC9111 - キャッシュの格納 27 格納許可条件チェック   ● Cache-Control のディレクティブや Expires ヘッダを確認 ○ 両ヘッダがなくてもヒューリスティックに キャッシュに格納することがある

Slide 28

Slide 28 text

キャッシュの利⽤ 28

Slide 29

Slide 29 text

● 「どのキャッシュを再利⽤して良いのか?」 ● 「キャッシュを使ってどのようなレスポンスを返せばいいのか?」 ● RFC9111の "4. Constructing Responses from Caches" に記載がある ○ https://www.rfc-editor.org/rfc/rfc9111#section-4 29 キャッシュの利⽤

Slide 30

Slide 30 text

● 基本的には「Freshならキャッシュを再利⽤して良い。Staleなら条件付きで再利⽤して良い」 ● Fresh ... キャッシュに保存されているレスポンスが、オリジンサーバーに問い合わせることなく(検証な しで)クライアントのリクエストに対して再利⽤できる状態にあること ● Stale ... キャッシュに格納されているレスポンスが、そのFreshness Lifetime(鮮度寿命)を超過している 状態。 Freshの逆 。 Freshではない状態 ● Freshness Lifetime ... オリジンサーバーによってレスポンスが⽣成されてから、いつまで検証なしで再利 ⽤して良いかを⽰す期間 Fresh と Stale RFC9111 - キャッシュの利⽤ 30

Slide 31

Slide 31 text

RFC9111 - キャッシュの利⽤ 31 キャッシュを利⽤する際のチャート

Slide 32

Slide 32 text

● URI、メソッド以外に、Varyヘッダで指定されて いるリクエストヘッダフィールドの⼀致を確認す る ○ キャッシュにはレスポンスだけではなくリ クエストヘッダ情報も必要 RFC9111 - キャッシュの利⽤ 32 保存しているキャッシュを取得  

Slide 33

Slide 33 text

● キャッシュされたレスポンスに no-cache ディレクティブが 含まれている場合、オリジンサーバーでの検証 (Validation)に成功しない限り、そのキャッシュレスポン スを利⽤してはいけない ○ Freshness判定以前の話 RFC9111 - キャッシュの利⽤ 33 Cache-Control: no-cache  

Slide 34

Slide 34 text

● freshness_lifetime と current_age を計 算してFreshと判定された場合、リクエストディ レクティブのハンドリングをしない限りはキャッ シュされたレスポンスを返却する RFC9111 - キャッシュの利⽤ 34 Freshness判定  

Slide 35

Slide 35 text

● Staleだからといって利⽤できないわけではない ○ Stale期間が max-stale の値以内であれば 使⽤可能 ○ オリジンサーバーへの検証結果によっては 使⽤可能 RFC9111 - キャッシュの利⽤ 35 Staleレスポンスの利⽤可否判定  

Slide 36

Slide 36 text

● RFC9111では、異なるディレクティブ間で意味が競合する場合の優先順位について明確な指針が⽰されて いる ● より厳しいディレクティブが尊重されるべき ○ If directives conflict (e.g., both max-age and no-cache are present), the most restrictive directive should be honored. ● Cache-Control: max-age=3600, no-cache とある場合、 no-cache のほうが制限が厳しいので採⽤ ● RFC7234では直接的な原則を定めた条項はない 競合するディレクティブの明確化 RFC9111 - キャッシュの利⽤ 36

Slide 37

Slide 37 text

● キャッシュしたレスポンスが利⽤可能かオリジンサーバー に問い合わせる ○ 304が返ってくるか ○ むしろ5xxが返ってきてしまうか RFC9111 - キャッシュの利⽤ 37 オリジンサーバーへの検証処理  

Slide 38

Slide 38 text

1. キャッシュしていたレスポンスを返却 2. オリジンサーバーへの検証時に得た新しいレスポ ンスを返却 3. (キャッシュミスによりオリジンサーバーからの レスポンスを返却) RFC9111 - キャッシュの利⽤ 38 レスポンスを返却  

Slide 39

Slide 39 text

● RFC9111では「いかなる条件下でも応答を再利⽤可能にするわけではない 」という点が明確化された ● RFC7234においては public private ディレクティブが「キャッシュの利⽤にも影響する」と誤解される ような記述だったらしい ○ 例: A private cache MAY store the response and reuse it for later requests, ... public private はあくまで格納を許可するディレクティブ RFC9111 - キャッシュの利⽤ 39

Slide 40

Slide 40 text

Tests for HTTP Caches 40

Slide 41

Slide 41 text

● This is a test suite for the behaviours of HTTP caches, including browsers, proxy caches and CDNs. ● https://cache-tests.fyi/ ○ https://github.com/http-tests/cache-tests ● RFC9111を参照しているテストスイート ○ HTTPキャッシュはその性質上、最適化⼿段である。実装は全てをキャッシュする必要はない。ただ し、キャッシュを⾏う場合、その動作は仕様書(RFC9111)によって制約される。(発表者訳) ● ローカルでもテストが可能(!!!) Tests for HTTP Caches RFC91111 41

Slide 42

Slide 42 text

● PASS率 58.1% (212/365)。思ったよりも低い!! ○ v0.14.0 ● 失敗しているテスト概要(⼀部) ○ Staleレスポンスの対応が不⼗分 ■ ETag や Last-Modified を使った条件付きリクエストの⽣成が不⼗分 ○ オリジンからの304 Not Modifiedレスポンスによるキャッシュ更新が未対応 ■ 条件付きリクエストが不⼗分なため ○ リクエストディレクティブに未対応 ○ Rangeリクエストに未対応 ● 全てのテストをPASSすれば良いものではないらしいし、失敗しているテストを⾒る限りゲートウェイ キャッシュとしての共有キャッシュ機構としては⼤きな問題にはならなそうだが厳しい Tests for HTTP Caches RFC91111 42 github.com/2manymws/rc/rfc9111 をテストしてみた

Slide 43

Slide 43 text

43 まとめ

Slide 44

Slide 44 text

● RFC9111の「キャッシュの格納」と「キャッシュの使⽤」についてフローチャートを元に紹介した ○ 「キャッシュの格納」は "3. Storing Responses in Caches" ■ https://www.rfc-editor.org/rfc/rfc9111#section-3 ○ 「キャッシュの利⽤」は "4. Constructing Responses from Caches" ■ https://www.rfc-editor.org/rfc/rfc9111#section-4 ● 発表者はRFC9111を参照した共有キャッシュHTTPミドルウェア github.com/2manymws/rc を開発した ○ RFC7234と⽐べて仕様が明確になっているので相対的に実装しやすい ■ 「RFC7234からの変更」は "Appendix B. Changes from RFC 7234" ● https://www.rfc-editor.org/rfc/rfc9111#appendix-B ● 皆さんのTests for HTTP Caches PASS率58.1%以上の実装をお待ちしています!! まとめ 44

Slide 45

Slide 45 text

本発表は主にGMOペパボ株式会社在籍時に取り組んだ内容になります。 この場をお借りして、⼼より感謝申し上げます。 謝辞 45

Slide 46

Slide 46 text

46 Thank you!!!

Slide 47

Slide 47 text

Appendix 47

Slide 48

Slide 48 text

以下の優先順位でFreshness Lifetime( freshness_lifetime )を決定 1. 共有キャッシュかつs-maxageディレクティブあり: s-maxageの値を使⽤ 2. max-ageディレクティブあり: max-ageの値を使⽤ 3. Expiresヘッダーあり: Expires - Dateの値を使⽤ 4. 明⽰的な期限なし: ヒューリスティック(推測的)な⽅法を適⽤可能 freshness_lifetime > current_age かどうかでFreshかStaleを決定 freshness_lifetimeの計算 Appendix 48

Slide 49

Slide 49 text

以下の⼿順でcurrent_ageを計算 1. apparent_age: max(0, response_time - date_value)
 a. サーバーがレスポンスを⽣成した時刻と受信時刻の差 2. response_delay: response_time - request_time
 a. ネットワーク遅延時間 3. corrected_age_value: age_value + response_delay
 a. Ageヘッダーの値にネットワーク遅延を加算 4. corrected_initial_age: max(apparent_age, corrected_age_value)
 a. 2つの計算⽅法の⼤きい⽅を採⽤(保守的なアプローチ) 5. resident_time: now - response_time
 a. レスポンスがローカルに保存されている時間 6. current_age: corrected_initial_age + resident_time
 a. 最終的な経過時間 current_ageの計算 Appendix 49