Mixed Content Level 2の議論 (2017年)

20190905 追記
Draft版の仕様が出てきたので「Mixed Content Level 2の仕様について - ASnoKaze blog」を書きました。


20181010 追記
Chromeが行っている取り組みについて、「Auto Upgrade Mixed Contentとは - ASnoKaze blog」を書きました。


W3CのWeb Application Security WGのco-chairとなった GoogleのMike West氏から、11月に実施されるTPAC 2017に先立って「Mixed Content Level 2」という提案が出されている。

Mixed Content

https://のURLで、http://のリソースを読み込むと警告・もしくはブロックを行うMixed Contentという仕様がある。そのMixed Contentの問題とそれを改善するMixed Content Level 2の議論が始まっている。

まだまだ議論のたたき台ではあるが、簡単に読んでおく。

問題点
  • 仕様でOptionally-blockableとして定義される、必ずしも読み込みがブロックされない画像などのリソースは、未だにhttp://で使用されているケースが有ります。警告などは表示されますが、その割を減らすには効果は弱いようです。
  • https://mixed.badssl.com/といったoptionally-blockableがある場合は、ChromeではURLバーの鍵マークを表示しません。blockable がブロックされた場合は鍵マークが表示されます。blockableとoptionally-blockableの区別をし続け、ユーザにとって区別出来るものでしょうか?
  • HTTPからHTTPSへの移行は大変です。Upgrade-Insecure-Requestsは部分的に問題を解決しますが、開発者がヘッダに指定する必要があります。HSTS PrimingはFirefoxで実装されていますが、その価値は示されていません。
提案
  • ユーザエージェントは、blockable mixed contentをブロックするのではなくアップグレードします。Upgrade-Insecure-Requestsがあるかのように、http://のリソースでもhttps://でアクセスします
  • ユーザエージェントは、optionally-blockable mixed contentをデフォルトでblockableとして扱います。そして上記のようにアップグレードが適応されます。開発者がヘッダなどで明示的にoptionally-blockableとして扱うように指示できるようにすることも検討する。
  • ユーザエージェントは、複雑さを避けるためにUpgrade-Insecure-Requestsを廃止する。
  • ユーザエージェントは、より多くの状況でURLバーの鍵マーク(盾マーク)を消すべきです

また、アップグレードしてもhttpsの接続性がない場合はタイムアウトするまでレンダリングがブロックされる可能性があるため、タイムアウト時間を短した方がいいとMLで意見が出ています。

Akamai Edge 2017 に行ってきた

10月11~13日に、ラスベガスで開催されたAkamai Edge 2017に参加してきました。

英語を聞きながらのメモなので断片的ではあるが、せっかくですので、面白かった所など紹介する。

オープニング・キーノート

Tom Leighton氏から、「Connect to Tomorrow」というイベントのキーフレーズが紹介されるとともに、Akamaiのこれまでとこれからについて紹介されました。

そして4つのテーマについて紹介があり、Akamaiの目指す方向が示されました

  • パフォーマンス
  • メディア
  • セキュリティ
  • DevOps

動画も公開されている
https://edge.akamai.com/ec/us/speakers/tom-keynote-2017.jsp

マルチソース・ストリーミング

中でもマルチソース・ストリーミングビデオプレーヤーのデモは非常に面白かった(現在beta)。

f:id:ASnoKaze:20171023235028p:plain

P2Pキャッシュのデモである

  • 4つのノートPCで動画をストリーミング再生する。緑のPCは、緑枠の画面。といった具合に、PCと対応するモニタが表示されている(赤紫緑黄の4色)。
  • 各モニタ画面右下の各四角は、動画のセグメントがどこから取得されたか示している。
    • オレンジはAkamaiから取得。それ以外は隣接するPC(色に対応)から取得していることを示している。

プレイヤーが判断し、一番早いところ取得するようになっている。(あまり聞き取れなかったので、違うかもしれないがどうやらWebRTCを使ってるよう?)


このデモでは無かったが、単純にマルチCDNで一番早いところからセグメントを取得してストリーミングを再生することも出来るようだ。もちろん、ソース毎の提供割合をコントロールできるようにもなっている

Bot Manager

もう一つ上げるとすればセキュリティーソリューションである。WAFのデモも非常に面白かったが、Bot Managerという製品のデモが面白かった。

ユーザの動作からBotを判別する機能だが、スマホブラウザのあらゆる情報を取得しBotかどうか判別している。
f:id:ASnoKaze:20171024000113p:plain
f:id:ASnoKaze:20171024000116p:plain

デモでは、各種傾きセンサ、タッチ・タップイベントを取っている例が示された。入力や傾き情報などを合わせてBot判定を行うようでかなり複雑な処理を行っていそうであった。

パフォーマンス関連のセッション

Akamaiらしくパフォーマンスに関するセッションは多くあった。手元にあるメモをかいつまんで紹介する。

Web Performance Roadmap 2017

各種サービスの新機能など

  • Image Manager
    • 画像最適化ソリューション。モバイルや回線速度によって、画像サイズやフォーマット(JPEG 2000,WebP... etc)、画像の品質を落とさず最適化を行う。
    • 一つのオリジナル画像を準備しておくだけで、自動で行われる。
    • 60%ほど品質を落とさずデータサイズを小さくした。13%ページの高速化に寄与した。
  • Ion4
    • Webページの最適化。意識せず高速化される。
    • HTTP/2, Brotli, OCSP Stapling, TLS1.3(Comming Soon)
    • Adaptive Learning Engine, Automatic Push: Akamai側が自動で学習し最適に配信する。HTTP/2サーバプッシュも、学習し最適なタイミングで実施される。
    • 近年はページごとのJavaScriptの増えているが、その最適も行う。32%ほどサイズが小さくなることも(Script Management)
  • Mobile App Performance SDK
    • モバイル・アプリケーション向けの最適化ソリューション
    • ネットワークレイテンシのレポート機能
    • 内部的に自動的にリソースのキャッシュ
    • AkamaiのEdgeサーバとの最適通信路の選択
  • Ad Recovery
    • Adブロッカーを回避して、広告を表示できる(AkamaiのURLから配信?仕組みはちょっとよくわからず)。動画広告でも同様の機能がある模様。


HTTP2の最適化の話はもっと詳しく聞きたいところではあった、、、

Faster Bytes is Not Always Enough - Why is The Web Slow? (And What Can We Do About It)

f:id:ASnoKaze:20171024004835j:plain
標準化界隈でも活発に活動されているYoav Weiss氏のセッション。セッション前に少しお話できた。

このセッションでは、パフォーマンス上の問題とその解決の役に立つ技術の紹介的なセッションだった。

  • 動的ページのサーバサイド処理時間
    • HTTP/2サーバプッシュを用いることで、生成中の待ち時間を有効活用できる
  • リソースのディスカバリ
    • rel=preconnect, preload を利用しリソースを早く取得できるようにする
    • preloadのいいところ: クロスオリジンも可、CacheやCookieが使える、コンテンツネゴシエーションが働く、loadとerrorイベントが取れる
    • pushのいいところ: より待ち時間が少ない
    • Early Hints
    • 「rel=prelaod ... as=scripts」指定でpriorityが使えるようになる
  • 画像/JSの肥大化
  • クライアントサイド処理
Media Acceleration and QUIC Optimization

Akamaiでは、動画の配信でGoogle QUICを使用している。

f:id:ASnoKaze:20171024005415j:plain

  • 24%リバッファリングを減少させ
  • 12.5%動画の配信を早くし
  • より高画質な動画を選択出来るユーザが増えた

また、360度映像で、視点の向いているところだけ高画質配信する事例紹介も。


Akamaiの環境では、QUICとTCPの映像配信の優劣比は

  • 60%の環境でQUICが良い
  • 20%はQUICとTCP環境が同等
  • 20%はTCP環境の方が良い

となっており、特定のTCPに最適化されたネットワーク環境下ではTCPの環境のほうが良いとのことだ。


また、ネットワーク環境が遅いところほどQUICの効果が高く、Akamai輻輳制御を改善したTuned QUICのほうが若干高速であることが示されている。
f:id:ASnoKaze:20171024010331j:plain

  • 青がTCP, オレンジがQUIC, 白がAkamaiチューニングQUIC
  • 左右グラフの内3グループあるのは、左が高速、中央が中速、右が低速な環境のグラフである。


現在はAkamaiGoogle QUICをサポートしているが、IETF QUICのサポートももちろん視野に入れているが、IETF版QUICの改版は早いため質とコストを見つつ対応していく予定のようだ。

その他

f:id:ASnoKaze:20171024011048j:plain

その他沢山の機能紹介があった。また、DevOps系のセッションも多く、Configurationのコード化や、フェイルオーバー、等運用にかかわる話も非常に濃かった。

Akamaiの方々にも当日お話させて頂く機会があり大変勉強になりました。
ありがとうございました。

ラスベガス

大変刺激的な街でした
f:id:ASnoKaze:20171024011945j:plain

Device Memory APIを用いてデバイスのメモリサイズを取得する

背景

Webサービスでは、デバイスの性能毎に軽量版の機能を提供することが有ります。

Google Search、Google Map、Facebookなどではローエンドデバイスでは一部軽量版のページを提供することが有ります。

または、クライアントサイドで何かしらの処理速度などを指標として収集している場合は端末の性能毎に分類・正規化する必要が出てきます。

Device Memory

W3Cでは「Device Memory」という仕様が議論されており、これはデバイスのメモリサイズを取得できる仕組みを提供します。

この仕様では2つの方法が定義されています

なお、サイズはギガバイトで、最も近い0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128のいずれかの値を返す事になっています。

JavaScriptから取得する

JavaScriptから下記で取得できます(httpsの場合のみ)。

navigator.deviceMemory

現在Chrome Canary(64.0.3245.0)では値を取得できます。
f:id:ASnoKaze:20171021022152p:plain

HTTPリクエストヘッダで通知する

もしくはメモリの情報を通知したいデバイスは、HTTPリクエストで通知することもできます。
HTTP Client Hints」の仕様に則り

サーバはレスポンスヘッダで、Accept-CHにDevice-Memoryを設定することで、この値をみて適切なコンテンツを提供できる旨クライアントに通知します。

Accept-CH: Device-Memory
Accept-CH-Lifetime: 86400


クライアントはDevice-Memoryヘッダで、メモリサイズ(単位はギガバイト)をサーバに通知します。

GET /example HTTP/1.1
Device-Memory: 0.75

WebSockets over HTTP2 の提案仕様。再び。(RFC8441)

2018/09/19追記
RFC 8441 として標準化されました

2018/03/10追記
WebSockets over HTTP2の更新分について、記事を別途書きました
asnokaze.hatenablog.com

2018/02/06追記
Bootstrapping WebSockets with HTTP/2」はWG Draftになり、この方向で標準化が進む方向です。ただし、下記のプロトコルフローとは異なりHEADERSフレームでWebSocketのアップグレードを処理する手順になっています。

Chromiumにおいても「Add field trial and command line flag for Websockets over HTTP/2.」というコミットが入っており、実装はまだですが進展がありそうです
https://chromium.googlesource.com/chromium/src/+/58b42322d7fd12e6909e95fe4bf63e29e83000da


下記の内容は古く、現在は違うアップグレード手順になっています


先日「Bootstrapping WebSockets with HTTP/2」という仕様が提出されています。

HTTP/2とWebSockets

HTTP/2の上でWebSocketsを通信することは出来ないのが現状です。

しかし根強くWebSockets over HTTP/2の議論は定期的に行われています。


古くはHTTP/2策定中であった2014年に「WebSocket over HTTP/2」という仕様の提案もありました。これはHTTP/2のレイヤにWebSocketsをマッピングする方式でした。ネゴシーエションやフレーミング、ストリーム管理にも言及しています。しかし、この提案仕様は標準化には至りませんでした。


2016年にも「WiSH: A General Purpose Message Framing over Byte-Stream Oriented Wire Protocols (HTTP)」として、WebSockets互換の拡張仕様が提案されています。こちらに関しては、当時の議事録を見ると「この議論をHTTPbis WGでするのは正しい場所ではない」というコメントが残っています。


毎年行われている、ブラウザベンダ, ミドルウェア実装者, CDN事業者, 大手WebサイトといったHTTPの実装者が議論を行うHTTPWorkshopにおいて、今年は「Future of WebSockets?」という発表があり、Chromeの統計情報とWiSHの紹介があったようです。


そこでWebSocketsについての議論があったようです。

Bootstrapping WebSockets with HTTP/2

そして、昨日MozillaのPatrick McManusから「Bootstrapping WebSockets with HTTP/2」という仕様が出ている。文字通り、HTTP/2上でWebSocketsをブートストラップする仕組みの提案仕様である。


HTTP/2ではCONNECTメソッドを使うことでトンネリングする機能があり(一部今回の仕様で変更する)、それを用いて一つのストリーム上でWebSocketsの通信路を開いてWebSocketsのデータをやりとりする。

CONNECTメソッドを用いた通信路の開設

まずサーバはこの仕様に対応している事を示す、ENABLE_CONNECT_PROTOCOL SETTINGSパラメータを送信する。

クライアントとはCONNECTメソッドと、:protocol疑似ヘッダを用いてWebSockets用の通信路を開設する。この時、通常のCONNECTでは使用できないがこの仕様では:path, および:schemeを指定する必要がある。

サーバのレスポンスを持って通信路が開設される。
f:id:ASnoKaze:20171018003037p:plain

WebSocketsの通信を開始する

その後、開設したストリーム上で、DATAフレームでWebSocketsのアップグレード手順を実施する。
サーバからの101ステータスコードが帰ってきた後、WebSocketsのデータのやりときができるようになる。

f:id:ASnoKaze:20171018003417p:plain

議論

HTTPbis WGのメーリングリストでこのドラフトは議論が始まっています。

疑似ヘッダの話や、upgrade手順の議論、クライアントからCONNECTリクエストとDATAフレームを一度に送れるのではないかといったフィードバックが出ています。

標準化が進むのかはわかりませんが、またしばらくWebSocketsの話は続きそうです。

TLS1.3とDC内での復号に関する熱い議論

各ブラウザや、OpenSSL・BoringSSLといった暗号ライブラリ、ミドルウェアのTLS1.3対応が進んでおり、実際に通信できるところまで来ている。

標準化としても大詰めを迎えている。

前回のIETFより話題に上がっている、TLS1.3に関するDC内での復号を目的とした議論について、新しく「TLS 1.3 Option for Negotiation of Visibility in the Datacenter」という拡張仕様が出ている。


これが割りと盛り上がっているので簡単に整理する

TLS1.3

拡張仕様の話に入る前に、簡単にTLS1.3の現状について触れる。

TLS1.3は、0-RTTハンドシェイクのサポートといったパフォーマンスの改善と、多くのセキュリティ改善が入ったTLSの次期バージョンである。

仕様としても長い間議論されており、draft-21まで出ている。ラストコールまで行ったが、色々の問題で2nd WGLCをやるかと行ったところである。


ただ、一つ大きな課題が残っている。初期より、経路上のファイアウォールなどによりブロックされるという疎通性の問題があり、ハンドシェイクにおけるバージョンネゴシエーションSupported Versions拡張を使用するようになっている。

しかし、それでも疎通性の問題が解決できておらず、前回のIETF99では、1~10%の環境で中間装置の問題で疎通が出来ないケースがあることがわかっている(資料)


疎通性の問題を解決するために、Googleが色々試している。その様子はChrome CanaryのTLS1.3の設定項目を見ると複数の実験バージョンが確認できる。具体的な実験内容は、BoringSSLの該当部分のコードを読む必要がありそうだ。

f:id:ASnoKaze:20171005003240j:plain

おそらく来月開催されるIETF100で、そこら辺の改善についての議論が進むものと思われる。

DC内復号の提案

さて、TLS1.3の標準化が大詰めを迎えるなか、IETFTLS WGでは、TLS1.3のDC内での復号というテーマが出ている。これは、DC内の通信でもTLSを使いながらも、秘密鍵を有していれば任意の通信を復号出来るようにするという要件が特定のエンタープライズ環境であるようで、それを可能にしたいという提案である。

TLS1.3と復号

TLS1.3ではephemeralな鍵交換のみがサポートされており、各セッションごとに暗号化に使用する一時鍵が異なっており、サーバに設定する秘密鍵を有していても復号できなくなっている。


復号しようとするならば、各セッションごとのその一時鍵(セッションシークレット)が必要になる。
最新のopensslであれば -keylogfile を指定することでそれらのパラメータをファイルに残すことも出来る(SSLKEYLOGFILE環境変数を設定することで、各ブラウザでも同様のログを出力可能)

$ openssl s_client -state -connect localhost:443 -keylogfile sslkeylog.log -tls1_3 
//略

$ cat ./sslkeylog.log
# SSL/TLS secrets log file, generated by OpenSSL
SERVER_HANDSHAKE_TRAFFIC_SECRET ce146098355b00754c6fd062f7e0dfdd982bf1b84a51deda78ffe217560852a5 c16440d97afd98db5f2b46c806f342e59e89e5c51c25f89c7614328bd32ab97556ff30c3545dda519fddca59acf95fbf
SERVER_TRAFFIC_SECRET_0 ce146098355b00754c6fd062f7e0dfdd982bf1b84a51deda78ffe217560852a5 0b2b1a6e4648883b0505c17ea6580f4d378a54d468d8b0a5dd07c4867725e1615191d7226d2e87e6f89866daa74ffb91
CLIENT_HANDSHAKE_TRAFFIC_SECRET ce146098355b00754c6fd062f7e0dfdd982bf1b84a51deda78ffe217560852a5 3fd9a41f47028ad33610ee7cbae09120c11b8e64d478ba73745c85078cbdbaa01673ef4589cdf4589c9b2fa22d8ac150
CLIENT_TRAFFIC_SECRET_0 ce146098355b00754c6fd062f7e0dfdd982bf1b84a51deda78ffe217560852a5 f8371e8d335b6e79c7e58be7ee26a6254b848311b320ddb37bc7a8b911ff34f22a812be2dd0e5aaae45208439100eba0

このkeylogfileファイルをWiresharkに設定すれば復号することも出来ます。

ただし、この場合は各サーバが各セッション毎に鍵情報を保管することになり、特定の鍵で任意の通信を復号することは出来ない。

IETF99 (7月)

そのような中で、エンタープライズ環境での影響についてIETF99で発表されたのが下記の資料である。
Impact of TLS 1.3 on Enterprise Network Operations


この発表では合わせて、「Data Center use of Static Diffie-Hellman in TLS 1.3」という、サーバからのkey_shareに静的な鍵を使うようにする提案も行われている。これによって、その鍵を持っていればすべてのセッションを複合できるようになります。

この発表は大変大きな議論を呼び、さらに米国政府関係者が提案を支持したことにもよって、会場のマイクの前には長蛇の列が出来るような状況になりました。もちろん、そのような機能でTLSを弱くすることは出来ないという主張である。


twitter上でも #tlsfight99 ハッシュタグで熱い意見が流れています。
twitter.com

最終的に会場内でこの議論に関する温度感が確認(hum)されましたが、この議論を続けるべきと反対派はだいたい半々でした。

新しい提案

以上の流れが会った上で、DC内で復号出来るようにする拡張仕様が、別の人によって提案がされています
https://tools.ietf.org/html/draft-rhrd-tls-tls13-visibility-00

TLS Visibility Extensionという拡張を定義されており、クライアントとサーバによってハンドシェイク時に合意され、使用されます。

SSWrapDH1とSSWrapDH2という2つの鍵を使用します。

  • SSWrapDH1: 通信を復号するのに使用する秘密鍵とその公開鍵
  • SSWrapDH2: 各セッションごとに使用される秘密鍵とその公開鍵


TLS1.3通信を行う際は

  • SSWrapDH1公開鍵とSSWrapDH2秘密鍵から鍵Keを生成する
  • Keを使用して、TLS 1.3セッションEarly SecretとHandshake Secret(セッションシークレット)を暗号化します。
  • Server HelloメッセージのTLS Visibility Extensionに、SSWrapDH1公開鍵(フィンガープリント)、SSWrapDH2公開鍵、および暗号化されたセッションシークレットの識別子を送信します。


復号する際は

  • TLS Visibility ExtensionからSSWrapDH1公開鍵を取得する
  • SSHWrapDH1秘密鍵とSSWrapDH2公開鍵を使用してKeを生成する
  • Keを使用して、TLS Visibility Extensionで運ばれたセッションシークレットを解読する
  • セッションシークレットを使用して、TLS 1.3セッションの復号化に必要なキーイングマテリアルを導出する


このようにして秘密鍵所持者によって任意の通信を複合出来るようになっている。
他の方法の検討や、Security Considarationsは仕様の中で記述されている。

引き続き来月開催されるIETF100で議論があるかもしれない。
さてさてどうなるのだろうか。

パスワードマネージャが適切にパスワードを生成できるようにするポリシーの提案仕様

パスワードの管理に、1PasswordやLastPassといったパスワードマネージャを使うのは一般的になってきています。

そのようなパスワードマネージャはランダムなパスワードを生成しますが、Webサービスによって使える文字の種類や、長さというのはマチマチです。

そこで、Webサービス側からパスワードの要件について記述できるようにする仕様がIETFに提出されています。

どこのWGで議論するのか、標準化が進むのかよくわからないが、とりあえず読んでおく。

Open Password Automation Recipe Protocol

パスワード自動生成出来るように提案されている仕様は「Open Password Automation Recipe (OPAR) Protocol」というタイトルです。

Webサービスはページ内に下記のようなJSONをOPAR_Policyという変数で埋め込むことで、自サービスのパスワード要件を宣言できます。

例えば以下のとおりである

   {
      "version":1,
      "min_length":8,
      "max_length":20,
      "numbers":{
        "allowed": true,
        "minimum": 2
      },
      "lowercase":{
        "allowed": true,
        "minimum": 2
      },
      "uppercase":{
        "allowed": true,
        "minimum": 2
      },
      "special_characters":{
        "allowed": true,
        "valid_characters": "+-_()*&^%$#@!?",
        "minimum": 2
      },
      "wide_characters":{
        "allowed": false,
        "minimum": 0
      },
      "include_extended_ascii": true
   }

各パラメータは以下のとおりです

  • version: 現状、バージョンは1
  • min_length: パスワードの最小長
  • max_length: パスワードの最大長
  • numbers: 数字を許可するか、および最低文字数を指定
  • lowercase: 小文字を許可するか、および最低文字数を指定
  • uppercase: 大文字を許可するか、および最低文字数を指定
  • special_characters: 記号類を許可するか、許可される記号のリスト、および最低文字数を指定
  • wide_characters: マルチバイト文字を許可するか、および最低文字数を指定
  • include_extended_ascii: キリル文字、アクセント記号付き西洋文字、ギリシャ文字など、拡張ASCII文字セットの文字を許可するか


XSSでこのオブジェクトを上書きされると、本来のポリシーに見合うものの弱いパスワードを発行させられたりするのかな?
と、思ったけどXSSあったらパスワード取られるから自動生成しててもリスクと脅威は変わらないか

キャッシュサーバの効率を改善するHTTP Variantsという提案仕様

HTTP Variants

IETFのHTTP WGやQUIC WGのチェアをしているmnot氏より、キャッシュの効率が改善する「Variants」というHTTPレスポンスヘッダを定義する「HTTP Variants」という提案仕様が出ています。

この機能は、Fastly VCLの機能の標準化のようです。

少々想定している背景がわかりづらいのですが、自分なりに簡単にまとめてみる。

背景

Webにおいて、サーバはクライアントからのリクエストヘッダを見てコンテンツを出し分けています。

例えば、Accept-Languageリクエストヘッダを見てコンテンツの言語を変更しています。キャッシュサーバももちろんこのAccept-Languageを見て、それぞれ毎にコンテンツをキャッシュする必要があります。

次の例を見てみましょう
f:id:ASnoKaze:20171001003447p:plain

  • 1. ブラウザは下記のHTTPリクエストを送信する
   GET /foo HTTP/1.1
   Host: www.example.com
   Accept-Language: en;q=1.0, fr;q=0.5

Accept-Languageヘッダで、英語もしくはフランス語のコンテンツを要求する

  • 2. リクエストを受け付けたオリジンサーバは、下記のHTTPレスポンスを送信する
   HTTP/1.1 200 OK
   Content-Type: text/html
   Content-Language: fr
   Vary: Accept-Language
   Transfer-Encoding: chunked

   [French content]

このオリジンサーバは英語のコンテンツは持っておらず、フランス語のコンテンツを返しています。Content-Languageヘッダでその旨が明示されています。また、Varyヘッダで、キャッシュサーバはAccept-Language毎にコンテンツをキャッシュすべきことを示しています。

問題点

この時の問題は、キャッシュサーバはオリジンサーバが結局どの言語のコンテンツを持っているか知ることが出来ない点です。

日本語もしくはフランス語を要求するユーザがいたらどうでしょうか?

Accept-Language: jp;q=1.0, fr;q=0.5

オリジンサーバが日本語に対応しているかはキャッシュサーバはわかりません。そのため、オリジンサーバはフランス語のコンテンツしか持ってなくても、キャッシュサーバはそのことを知らないので、そのままリクエストをオリジンサーバに転送します。

キャッシュサーバはそのリクエストをオリジンサーバに送った後に、「Content-Language: fr」のHTTPレスポンスヘッダを受け取り、日本語には対応してないことを知ります。キャッシュしてあるフランス語のコンテンツを使えたはずなのに、オリジンサーバに問い合わせしてしまってます。

上記の例は、言語でしたが、Accept-Encodingヘッダでも同様です。

このような手間が発生しないように、オリジンサーバが対応している言語(コンテンツ)をキャッシュサーバに伝達出来るようにするのが「HTTP Variants」の仕組みです。

HTTP Variantsヘッダ

Variantsヘッダは、オリジンサーバがキャッシュサーバに自身がどのようなコンテンツを提供できるか明示できます。

HTTPレスポンスのヘッダに下記のように追加します。

Variants: Content-Language;fr;de

このオリジンサーバは、フランス語とドイツ語が提供できるということを意味しています。このVariantsヘッダが付いたHTTPレスポンスをキャッシュサーバが受けとることで、オリジンサーバが対応しているコンテンツを知ることが出来ます。

つまり、先の例で言えば日本語のコンテンツは提供していないので、日本語のコンテンツが要求されてもキャッシュしてあるフランス語のコンテンツを使えるということです。

以下のように、複数のVariantsを羅列することも出来ます。

   HTTP/1.1 200 OK
   Content-Type: image/gif
   Content-Language: en
   Content-Encoding: br
   Variants: Content-Language;en;jp;de
   Variants: Content-Encoding;br;gzip
   Vary: Accept-Language, Accept-Encoding
   Transfer-Encoding: chunked


このVariantsで指定されたヘッダのセマンティクスを理解しているサーバは、Varyヘッダを無視しなければなりません(MUST)。Variantsヘッダを用いることで、Varyヘッダで指定されたヘッダ毎にキャッシュする必要はなくなります。