
はじめに
現代の Web アプリケーションにおいて、パフォーマンスはユーザー体験を左右する重要な要素です。これまで、gzip、Brotli、Zstandard といった優れた圧縮技術が、サーバーからクライアントへのデータ転送量を削減し、Web サイトの高速化に貢献してきました。
しかし、SPA(シングルページアプリケーション)や API 駆動型のアーキテクチャが主流となる中で、新たな課題が浮上しています。それは、数十 KB 程度の JSON データや動的に読み込まれる UI コンポーネントといった、比較的小さな HTTP レスポンスにおいて、圧縮効率が上がりにくいという問題です。既存の圧縮アルゴリズムは、データ内にある程度の繰り返しパターンが存在しなければ、その効果を十分に発揮できないという特性があるためです。
この課題を解決すべく登場したのが、新しい Web 標準仕様「Compression Dictionary Transport」です。
この記事では、この Compression Dictionary Transport の PHP への実装について解説します。
- php-ext-brotli: PHP の Brotli 圧縮拡張機能
- php-ext-zstd: PHP の Zstandard 圧縮拡張機能
発想の転換:「繰り返しパターン」を事前に共有する
Compression Dictionary Transport の核心的なアイデアは、非常にシンプルです。
圧縮したいデータの中に繰り返しパターンが少ないなら、サーバーとブラウザの間で、あらかじめ「共通の辞書」を共有しておけば良い。
この「辞書」とは、サイトで頻繁に使われる文字列、HTML タグの構造、JavaScript の定型コード、CSS のクラス名など、予想される繰り返しパターンを集めたファイルです。 これを事前に共有することで、たとえ小さなデータであっても、辞書内のパターンと一致する部分を効率的に参照へと置き換え、驚異的な圧縮率を実現します。
Compression Dictionary Transport の仕組み
Compression Dictionary Transport1 は現在、IETF で標準化が進められており、主要なブラウザでの実装も始まりつつあります。
その基本的なワークフローは、Brotli 実装と Zstandard 実装で共通しており、HTTP ヘッダを介したサーバーとクライアントの連携によって成り立っています。
主要なHTTPヘッダー
| ヘッダー | 役割 |
|---|---|
Accept-Encoding |
クライアントが辞書圧縮 (dcb, dcz など) をサポートしていることをサーバーに通知します。 |
Use-As-Dictionary |
サーバーが辞書そのものを配信する際に使用し、ブラウザにこれを辞書としてキャッシュするよう指示します。 |
Available-Dictionary |
クライアントがキャッシュ済みの辞書のハッシュ値をサーバーに通知し、利用可能であることを伝えます。 |
Content-Encoding |
サーバーがレスポンスを辞書圧縮したことを示します (dcb または dcz)。 |
Vary |
Accept-Encoding, Available-Dictionary を指定し、プロキシキャッシュなどが正しく動作するようにします。 |
基本的なワークフロー
- 辞書の配布: サーバーが
Use-As-Dictionaryヘッダーを付けて辞書リソースを配信する。 - 辞書の保存: ブラウザは辞書を保存し、その内容から SHA-256 ハッシュ値を計算する。
- 辞書の利用通知: 次回以降のリクエストで、ブラウザは
Available-Dictionaryヘッダーに計算したハッシュ値を含めて送信する。 - 辞書圧縮の実行: サーバーは
Available-Dictionaryヘッダーを受け取ると、自身が保持する辞書のハッシュ値と照合する。一致すれば、その辞書を使ってレスポンスを圧縮する。 - レスポンスの返却: サーバーは
Content-Encodingヘッダー(例:dcb,dcz)を付与して、辞書圧縮されたデータをクライアントに返す。 - データの伸張: レスポンスを受け取ったブラウザは、指定された共有辞書を使ってデータを正しく伸張(解凍)し、元のデータを復元する。
この仕組みは、特に以下のようなユースケースで絶大な効果を発揮します。
- SPA: アプリケーションのバージョンごとに JS バンドルから辞書を生成し、API レスポンスの圧縮に利用する。
- 頻繁に更新されるライブデータ: 共通するデータ構造を辞書として共有し、差分だけを効率的に転送する。
- マイクロサービス間のAPI通信: サービス間で共通の辞書を用意し、通信ペイロードを削減する。
Brotli による実装 (Dictionary-Compressed Brotli)
php-ext-brotli では、Content-Encoding: dcb として Compression Dictionary Transport を実装します。
DCB バイナリフォーマット
dcb で圧縮されたデータは、仕様に従ったフォーマットで構築されます。
+--------------+-------------------------------+------------------+ | Magic Header | Dictionary SHA-256 Hash | Compressed Data | | (4 bytes) | (32 bytes) | (variable) | +--------------+-------------------------------+------------------+ | 0xff444342 | SHA-256 of dictionary content | Brotli compressed| | | | with dictionary | +--------------+-------------------------------+------------------+
PHP での実装詳細 (php-ext-brotli)
実装は、PHP の出力バッファリング (ob_start) をフックする形で行われます。
1. 設定と有効化
php.ini または ini_set で辞書へのパスを指定し、ob_start でハンドラを登録します。
<?php // 辞書圧縮 (dcb) を使うための辞書ファイルへの絶対パスを指定 ini_set('brotli.output_compression_dictionary', '/path/to/your/shared.dict'); // スクリプトの冒頭でハンドラを登録 ob_start('ob_brotli_handler'); // これ以降の出力が自動的に辞書圧縮の対象となる echo "Hello"; ?>
2. C言語レベルのコアロジック
内部では、以下の処理が実行されます。
- エンコーディングの解析:
Accept-Encodingヘッダを解析し、dcbが指定されているかをビットフラグで管理する。 - 辞書の検証:
HTTP_AVAILABLE_DICTIONARYヘッダの値と、サーバー側で指定された辞書から計算した SHA-256 ハッシュ値を比較する。ハッシュが一致しない場合は、安全に通常の Brotli 圧縮 (br) にフォールバックする。 - Brotliエンジンへの辞書アタッチ: Brotli のエンコーダーに辞書をセットする。
- DCBフォーマットの構築: 圧縮後、レスポンスの先頭にマジックヘッダーと辞書ハッシュを追加して、最終的な
dcbフォーマットのデータを作成する。
/* brotli.c: 辞書検証のロジック(簡略版) */ zval *available = zend_hash_str_find(..., "HTTP_AVAILABLE_DICTIONARY", ...); if (available) { // サーバー側辞書の SHA-256 ハッシュを計算 PHP_SHA256_CTX context; PHP_SHA256Init(&context); PHP_SHA256Update(&context, ...); PHP_SHA256Final(ctx->dict_digest, &context); // Base64 エンコードしてクライアントからのハッシュと比較 zend_string *b64 = php_base64_encode(ctx->dict_digest, 32); if (memcmp(ZSTR_VAL(b64), Z_STRVAL_P(available) + 1, ...)) { // 不一致の場合は dcb フラグを解除し、br 圧縮にフォールバック BROTLI_G(compression_coding) &= ~PHP_BROTLI_ENCODING_DCB; } } /* brotli.c: 圧縮コンテキストへの辞書適用(簡略版) */ if (dict) { // dict は読み込まれた辞書データ // 辞書データを準備 ctx->dictionary = BrotliEncoderPrepareDictionary(BROTLI_SHARED_DICTIONARY_RAW, ZSTR_LEN(dict), ZSTR_VAL(dict), BROTLI_MAX_QUALITY, NULL, NULL, NULL); // 準備した辞書をエンコーダーにアタッチ if (ctx->dictionary == NULL || !BrotliEncoderAttachPreparedDictionary(ctx->encoder, ctx->dictionary)) { // エラー処理 return FAILURE; } }
このフォールバック機構により、Compression Dictionary Transport が利用できない環境でもアプリケーションは問題なく動作します。
Zstandard による実装 (Dictionary-Compressed ZStandard)
php-ext-zstd では、Content-Encoding: dcz として Compression Dictionary Transport を実装します。
DCZ バイナリフォーマット
dcz で圧縮されたデータは、仕様に従ったフォーマットで構築されます。
+--------------------+-------------------------------+------------------+ | Magic Header | Dictionary SHA-256 Hash | Compressed Data | | (8 bytes) | (32 bytes) | (variable) | +--------------------+-------------------------------+------------------+ | 0x5e2a4d1820000000 | SHA-256 of dictionary content | ZSTD compressed | | | | with dictionary | +--------------------+-------------------------------+------------------+
PHP での実装詳細 (php-ext-zstd)
こちらも ob_start を利用した出力ハンドラとして実装されています。
1. 設定と有効化
php.ini または ini_set で辞書へのパスを指定し、ob_start でハンドラを登録します。
<?php // 辞書圧縮 (dcz) を使うための辞書ファイルへの絶対パスを指定 ini_set('zstd.output_compression_dict', '/path/to/your/dictionary.txt'); // スクリプトの冒頭でハンドラを登録 ob_start('ob_zstd_handler'); // これ以降の出力が自動的に辞書圧縮の対象となる echo "Hello"; ?>
2. C言語レベルのコアロジック
基本的な流れは Brotli 実装と似ていますが、Zstandard ライブラリの機能を使って実装されています。
- エンコーディングの解析: 同様に
Accept-Encodingを解析しdczフラグを管理する。 - 辞書の検証:
HTTP_AVAILABLE_DICTIONARYヘッダとサーバー側辞書のハッシュ値を比較する検証ロジックも同様である。不一致の場合は通常のzstd圧縮にフォールバックする。 - Zstandardエンジンへの辞書適用:
ZSTD_createCDictで辞書オブジェクトを作成し、ZSTD_CCtx_refCDictで圧縮コンテキストにセットする。 - DCZフォーマットの構築: 圧縮ストリームの先頭に、仕様で定められた 8 バイトのマジックヘッダーと 32 バイトの辞書ハッシュを書き込む。
/* zstd.c: 辞書検証のロジック(簡略版) */ zval *available = zend_hash_str_find(..., "HTTP_AVAILABLE_DICTIONARY", ...); if (available) { // サーバー側辞書の SHA-256 ハッシュを計算 PHP_SHA256_CTX context; PHP_SHA256Init(&context); PHP_SHA256Update(&context, ...); PHP_SHA256Final(ctx->dict_digest, &context); // Base64 エンコードしてクライアントからのハッシュと比較 zend_string *b64 = php_base64_encode(ctx->dict_digest, 32); if (memcmp(ZSTR_VAL(b64), Z_STRVAL_P(available) + 1, ...)) { // 不一致の場合は dcz フラグを解除し、zstd 圧縮にフォールバック ZSTD_G(compression_coding) &= ~PHP_ZSTD_ENCODING_DCZ; } } /* zstd.c: 圧縮コンテキストへの辞書適用(簡略版) */ if (dict) { // dictは読み込まれた辞書データ ctx->cdict = ZSTD_createCDict(ZSTR_VAL(dict), ZSTR_LEN(dict), (int)level); if (!ctx->cdict) { // エラー処理 return FAILURE; } ZSTD_CCtx_refCDict(ctx->cctx, ctx->cdict); }
テスト
compression-dictionary-transport-shop-demo というデモを使用してテストを行います。
1. ファイルの準備
まず、デモ用のファイルをいくつか変更・作成します。
public/index.html の末尾にある辞書のリンクを、PHP で配信するように変更します。
- <link rel="dictionary" href="/dictionary" /> - <link rel="compression-dictionary" href="/dictionary" /> + <link rel="compression-dictionary" href="./dictionary.php" />
public/dictionary.php を以下の内容で作成します2。
<?php // 辞書の配布用のレスポンスを生成 $file = __DIR__ . '/items/shop.dict'; // 辞書ファイルのパス header('Use-As-Dictionary: match="/items/*"'); // この辞書を適用するパスを指定 header('Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT'); header('Content-Type: application/octet-stream'); header('Content-Length: ' . filesize($file)); header('Accept-Ranges: bytes'); readfile($file);
次に、デモ用の辞書とコンテンツを配置します。
data/shop.dictをpublic/items/shop.dictにコピーする。data/*.htmlをpublic/items/*.phpとしてコピーする。
コピーした各 PHP ファイル(1f3bf.phpなど)の先頭に、設定を読み込むための include 文を追加します。
+ <?php include __DIR__ . '/header.php'; ?>
<!DOCTYPE html>
<!--
Copyright 2022 Google LLC
圧縮設定を記述する public/items/header.php を以下の内容で作成します。
<?php // Brotli (dcb) による辞書圧縮の設定 // // クライアントが dcb をサポートしている場合のみ、辞書ファイルを指定します。 // これにより、dcb 非対応のクライアントには通常の Brotli(br)圧縮が適用されます。 if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'dcb') !== false) { ini_set('brotli.output_compression_dict', __DIR__ . '/shop.dict'); } ob_start('ob_brotli_handler'); // // Zstandard (dcz) を試す場合はこちらを有効化します // if (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'dcz') !== false) { // ini_set('zstd.output_compression_dict', __DIR__ . '/shop.dict'); // } // ob_start('ob_zstd_handler');
最終的に、public ディレクトリは以下のような構成になります。
public
|-- about.html
|-- dictionary.php
|-- index.html
|-- items
| |-- 1f3bf.php
| |-- 1f45e.php
| |-- (他のアイテムPHPファイル)...
| |-- header.php
| `-- shop.dict
`-- static
|-- (省略)...
この public ディレクトリをドキュメントルートとして、PHP が動作する Web サーバーを起動します。
2. 動作確認
Compression Dictionary Transport は Chrome が先行してサポートしているため、Chrome ブラウザで確認します。
まず index.html にアクセスします。ページと共に dictionary.php が読み込まれ、辞書がブラウザに登録されます。登録された辞書は chrome://net-internals/#sharedDictionary で確認できます。

次に、商品アイテムページ(items/1f45e.phpなど)にアクセスします。Chrome の開発者ツールでネットワークリクエストを確認すると、以下のように動作していることがわかります。
- リクエストヘッダに辞書圧縮をサポートすることを示す
Accept-Encoding: ..., dcb, ...が含まれている。 - リクエストヘッダに利用可能な辞書を通知する
Available-Dictionaryが含まれている。 - レスポンスヘッダに
dcbで圧縮されたことを示すContent-Encoding: dcbが返されている。

Content-Encoding が適用された後のコンテンツサイズを比較すると、その効果は一目瞭然です。

通常の Brotli (br) 圧縮と比較しても、辞書圧縮 (dcb) ではデータサイズが大幅に削減されていることがわかります。
課題と展望
課題
- ブラウザサポート: 2025 年現在、Compression Dictionary Transport はまだ実験的な段階にあり、Chrome が先行している状況である。普及には他のブラウザでの標準サポートが待たれる。
- 辞書管理: サイトのコンテンツに合わせて最適な辞書をどのように生成し、更新していくかという運用戦略が重要になる。
- キャッシュ戦略:
Vary: Accept-Encoding, Available-Dictionaryヘッダーを正しく扱える CDN やプロキシの設定が必要である。 - セキュリティ: 辞書のハッシュ検証は、意図しないデータ展開を防ぐための重要なセキュリティ機構である。実装ではこの検証が必須となる。
展望
- 自動辞書生成: AI や機械学習を用いて、サイトのコンテンツから最適な辞書を自動生成するようなツールの登場が期待される。
- 標準化の進展: IETF での標準化が完了し、主要なブラウザや CDN でのサポートすることで、普及が進む。
- さらなる応用: Web コンテンツ配信だけでなく、マイクロサービス間の通信ペイロード削減など、活用の幅が広がっていくだろう。
まとめ
Compression Dictionary Transport は、Web パフォーマンスを次のレベルへ引き上げる可能性を秘めた、非常に有望な技術です。
今回、PHP の拡張機能である php-ext-brotli と php-ext-zstd の両方にこの機能が実装されたことで、PHP ユーザーがこの最先端技術の恩恵を受ける道筋が拓かれました。
実装の技術的ハイライト:
- 透過的な実装:
ob_startやphp.iniの設定により、既存のアプリケーションコードをほぼ変更することなく導入できる。 - 堅牢なフォールバック: 辞書圧縮が使えない環境でも、自動的に通常の Brotli/Zstandard 圧縮に切り替わる安全な設計である。
- 仕様準拠: IETF で策定中の仕様に基づいた、正確なバイナリフォーマットとヘッダー処理が実装されている。
ぜひこの新しい圧縮技術を試し、その効果を体感してみてください。
- MDN Web Doc: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Compression_dictionary_transport↩
- 辞書ファイルを配布すればよいだけなので PHP のような動的処理ではなくて Web サーバーから配布するようにしても問題ない(Use-As-Dictionary ヘッダは必要)↩