問題と背景
あるドキュメントサイトでは、リリースごとに類似したHTML、CSS、JavaScriptが生成されます。チームは、重複するバイト数を削減するために、後続のレスポンスが以前のレスポンスをBrotliまたはZstandardのディクショナリ(辞書)として再利用することを望んでいますが、ブラウザのサポートは一様ではなく、一部のレスポンスにはユーザーのプライベートデータが含まれています。Compression Dictionary Transportを使用したカナリアリリース、キャッシュ、およびセキュリティの計画を設計してください。
RFC 9842では、レスポンスがUse-As-Dictionaryでディクショナリを通知し、クライアントがAvailable-Dictionaryで利用可能なディクショナリを提示し、双方がディクショナリのコンテンツエンコーディングを交渉するフローが定義されています。この仕様ではHTTPSセキュアコンテキストが必須とされており、MDNでは現在この機能に「利用制限あり(Limited availability)」のラベルが付けられているため、すべてのブラウザに対して必須のパスとすることはできません。
面接官が見ているポイント
- ディクショナリの登録、マッチング、ハッシュ交渉、エンコーディングの選択、および通常の圧縮へのフォールバックを説明できるか?
- 鮮度(Freshness)、キャッシュキー、リリースバージョン、CDN、およびノード間の一貫性を適切に処理できるか?
- 共有ディクショナリに起因するコンテンツ推論や圧縮サイドチャネルのリスクを認識できるか?
- ブラウザ機能、HTTPS、およびレスポンスの可読性を考慮したプログレッシブエンハンスメントを設計できるか?
- エラーやプライバシー露出を増加させることなく、バイト削減効果を実証できるか?
事前に確認すべき質問
- 対象のブラウザ、WebView、プロキシ、およびCDNは関連するディクショナリエンコーディングをサポートしているか?ロールアウトをChromiumのみに限定することは可能か?
- どのレスポンスがパブリック、同一オリジン(Same-origin)、かつ重複が多いものであり、どれがユーザー、テナント、または認証データを含んでいるか?
- ディクショナリの作成、署名、失効、およびロールバックは誰が行うのか?また、リリースバージョンはリソースハッシュにバインドされているか?
- キャッシュは言語、テナント、認証状態、およびコンテンツエンコーディングごとに分離されているか?
- サイトはすでにBrotli、Zstandard、ETag、Early Hints、またはService Workerキャッシュを使用しているか?
30秒での回答
「ブラウザの機能と機密性に基づいて、パブリックで同一オリジンの極めて重複が多いリソースを選択します。HTTPS経由で、サーバーはUse-As-Dictionaryを使用してバージョン管理されたディクショナリを通知し、Available-Dictionaryの受信後に、dcb、dcz、または通常のBrotli/gzipを選択します。ディクショナリとコンテンツのバージョン、ハッシュ、およびキャッシュバリアントは分離された状態を維持し、機密性の高いレスポンスはディクショナリを共有しません。Chromiumと小規模なリソースセットを対象にカナリアリリースを実施し、バイト数、デコードエラー、キャッシュヒット、およびプライバシーアラートを測定し、サポートがない場合や検証に失敗した場合は常に通常のエンコーディングにフォールバックします。」
ステップごとの詳細解説
- 適切なリソースを選択する。 静的でパブリック、同一オリジンであり、バージョンが安定しているリソースから始めます。パーソナライズされたHTML、アカウントデータ、テナントをまたぐレスポンス、およびシークレットは除外します。追加される複雑さを受け入れる前に、重複度合いとディクショナリによるメリットを測定してください。
- 交渉フローを構築する。 レスポンスは
Use-As-Dictionaryを使用して、マッチ条件、タイプ、識別子、および鮮度を宣言します。マッチするディクショナリを持つクライアントはAvailable-Dictionaryハッシュを送信し、Accept-Encodingでディクショナリエンコーディングを通知します。サーバーは、双方がサポートしディクショナリが新鮮である場合にのみdcbまたはdczを返し、それ以外の場合は通常のエンコーディングを使用します。
HTTP/2 200
Content-Type: text/javascript
Cache-Control: public, max-age=3600
Use-As-Dictionary: match="/assets/*", id="docs-v42", type="dictionary"
HTTP/2 200
Content-Encoding: dcb
Vary: Accept-Encoding, Available-Dictionary- 一貫性の境界を固定する。 ディクショナリID、リソースバージョン、およびコンテンツハッシュは、単一のリリース成果物(Artifact)に含めます。すべてのCDNノードが同じディクショナリを取得する必要があり、半分のノードが古いディクショナリを返し、残りが新しいエンコーディングを使用するような状態にしてはなりません。
Varyとキャッシュキーは、表現を変更するリクエストヘッダーをカバーし、ディクショナリレスポンスが非対応のクライアントに届かないようにする必要があります。
- 鮮度とロールバックを処理する。 ディクショナリが期限切れになったり、失効したり、コンテンツバージョンと一致しなくなった場合は、通知を停止して通常の圧縮にフォールバックします。古いディクショナリは、厳格な廃棄期限を定めて管理されたオーバーラップ期間の間保持します。ロールバックでは、通知ヘッダーを削除し、CDNバリアントをパージして、Brotli/gzipを復元する必要があり、クライアントによるキャッシュクリアに依存してはなりません。
- プライバシーリスクを隔離する。 ディクショナリコンテンツは、同一オリジンのパブリックリソースと同様に保護します。攻撃者が入力の一部を制御でき、圧縮後のサイズを観測できる場合、繰り返される部分文字列によってディクショナリやレスポンスのコンテンツが漏洩する可能性があります。シークレットを攻撃者が制御するテキストと同じ圧縮コンテキストに入れないようにし、必要に応じてディクショナリ圧縮を無効化します。HTTPSはトランスポートを保護しますが、圧縮サイドチャネルを排除するわけではありません。
- プログレッシブエンハンスメントを使用する。 機能の欠如や交渉の失敗が発生した場合は、Brotli、gzip、または非圧縮のレスポンスを継続して使用します。まずは小規模な静的リソースとブラウザのコホートから開始します。
Content-Encodingの分布、転送バイト数、TTFB、デコードエラー、キャッシュヒット、およびフォールバック率を比較し、メリットが安定しない場合は機能を停止します。
模範回答
最初のロールアウトは、パーソナライズされたHTML、テナントデータ、およびシークレットを除外した、重複度の高いパブリックで同一オリジンのバージョン管理された静的リソースに限定します。HTTPS経由で、サーバーはUse-As-Dictionaryを通じてID、マッチスコープ、有効期限を含むディクショナリを通知します。クライアントがAvailable-Dictionaryを送信し、Accept-Encodingを交渉した後にのみ、サーバーはdcbまたはdczを返します。非対応のクライアント、古いディクショナリ、およびハッシュの不一致の場合は、通常のBrotli/gzipを使用します。
リリース成果物には、ディクショナリ、リソースハッシュ、CDNキャッシュバリアントを含め、すべてのノードで一貫性を保ちます。Varyは、エンコーディングとディクショナリのリクエストヘッダーを分離します。攻撃者が制御する入力とシークレットを同じ圧縮コンテキストに配置することは決してありません。HTTPSはサイズに基づくサイドチャネルを排除しないためです。カナリアはChromiumと小規模な静的セットから開始し、削減バイト数、キャッシュヒット、デコードエラー、フォールバック、プライバシーアラートを測定します。ロールバック時には、ディクショナリの通知を削除し、通常のエンコーディングを復元します。
よくある間違い
- 現象: すべてのレスポンスで共有ディクショナリを有効にする → 失敗の理由: パーソナライズされたデータや機密データが推論可能な圧縮コンテキストに入ってしまう → 修正方法: パブリックな静的リソースのみを使用し、機密性の高いレスポンスには通常の圧縮を使用する。
- 現象:
Accept-Encodingのみをチェックし、ディクショナリのハッシュや鮮度を無視する → 失敗の理由: クライアントが誤ったディクショナリを使用したり、デコードに失敗したりする可能性がある → 修正方法: ID、ハッシュ、バージョン、および有効期限をバインドする。 - 現象: CDNでURLのみによってキャッシュする → 失敗の理由: ディクショナリレスポンスが非対応のクライアントに配信される可能性がある → 修正方法:
Varyとキャッシュキーを使用して、エンコーディング、ディクショナリヘッダー、およびリソースバージョンを分離する。 - 現象: HTTPSを完全なセキュリティ保証として扱う → 失敗の理由: 圧縮サイドチャネルによって繰り返される部分文字列が依然として漏洩する可能性がある → 修正方法: 攻撃者が制御する入力をシークレットから隔離し、必要に応じてディクショナリ圧縮を無効化する。
フォローアップの質問と回答
ブラウザがサポートしていない場合はどうなりますか?
サーバーは、ディクショナリ交渉が成功した後にのみdcbまたはdczを返します。その他のリクエストはBrotli、gzip、または非圧縮を継続して使用します。フォールバックを監視し、ページへのアクセスにアップグレードを必須としないようにしてください。
ディクショナリの有効期間はどのくらいにすべきですか?
リリースの頻度、重複によるメリット、および失効速度に基づいて有効期間を設定し、その決定をリリース方針に組み込みます。静的バージョンの変更時には短いオーバーラップ期間を設け、その後古いディクショナリを無期限に保持するのではなく、廃棄してCDNバリアントをパージします。
デコードとキャッシュの正確性をどのようにテストしますか?
対応および非対応のブラウザ、HTTP/1.1、HTTP/2、異なるCDNノード、ならびにコールドキャッシュおよびウォームキャッシュでテストします。圧縮率だけでなく、Vary、コンテンツハッシュ、デコードされたバイト数、304レスポンス、オリジンフェッチ、および通常のエンコーディングへのフォールバックを検証します。
どのようなシグナルがあれば直ちに無効化すべきですか?
ユーザー間でのコンテンツの混入、ディクショナリハッシュの不一致、デコードエラー、キャッシュポイズニング、異常な圧縮サイズ、またはプライバシースキャンアラートが発生した場合は、直ちにUse-As-Dictionaryを削除する必要があります。通常のエンコーディングを復元し、インシデントのメトリクスを保存してください。
参考資料
- Compression Dictionary Transport (RFC 9842)
- MDN Compression Dictionary Transport
- Chrome for Developers: 圧縮辞書による Google 検索の改善
- Chromium Compression Dictionary Transport ドキュメント
面接チェックリスト
パブリックな静的リソースの選択と交渉ヘッダーから始めます。次に、ディクショナリのバージョン、キャッシュキー、HTTPS、サイドチャネル、プログレッシブエンハンスメント、およびロールバックについて説明します。バイト数、キャッシュ、エラーのメトリクスを用いてメリットを検証してください。
一行のまとめ
共有ディクショナリは、リソースの分離、バージョンハッシュ、ブラウザフォールバック、およびプライバシー保護策がすべて整っている場合にのみ、重複バイトを削減できます。