圧縮はどこで・何回やるか ― 事前圧縮が効く構成と、CDN に任せる構成
このシリーズは配信を軽くする話を続けてきました。前々回は ETag で再検証を安く(CDN と ETag を入れたのにサーバ負荷が上がった ― 動的生成と CDN はなぜ相性が悪いのか)、前回は キャッシュバスティングで「変えた分だけ」失効(更新したのに古い CSS が表示される ― キャッシュバスティングを content hash で正しくやる)。最後は 圧縮です。ただ正直に言うと、圧縮は前 2 回ほど「効く・効かない」がはっきりしません ―― 圧縮自体は安く、効き目は構成しだいだからです。その線引きを正直に見ていきます。
圧縮は安い ― でも 2 つの条件で話が変わる
まず誤解を解きます。gzip の圧縮はほぼタダです。典型的なページ(数十〜100KB のテキスト)なら 1 リクエストあたり 1ms 未満で、「gzip を有効にしたら CPU が跳ねる」は普通は起きません。では、わざわざ 事前に 圧縮して置いておく意味はどこにあるのか。話が変わるのは次の 2 つの条件です。
- brotli を最高品質で効かせたいとき ―― brotli は品質を上げるほど縮みますが、最高品質はとても遅く、リクエストのたびにかけるのは現実的ではありません。事前なら「1 回だけ」なので、最高品質で時間をかけられます。
- CDN を前に置かないとき ―― CDN があればエッジが圧縮を肩代わりしますが、無い構成では オリジンが全リクエストを捌きます。1 回 1ms でも、アクセスが増えれば積もる。事前圧縮なら、その都度の圧縮をまるごと無くせます。
逆に言えば、gzip だけ・かつ CDN 前段あり なら、事前圧縮の旨みはほとんどありません。効くのは「brotli を本気で使う」「CDN を挟まない」のどちらか(または両方)のときです。
だから「公開時に 1 回だけ・最高設定で」作り置く
静的配信なら、圧縮を 公開時の 1 回 に移せます。ページを生成するときに、圧縮済みファイル(.gz と .br)も一緒に作って置いておく。配信時は その圧縮済みをそのまま返すだけ です。1 回しかやらないので、最高設定(gzip は最大レベル、brotli は最高品質。brotli はテキストで gzip より 15〜25% 小さい)で時間をかけられます。速度優先で品質を落とさざるを得ない動的圧縮とは対照的です。そもそも公開すると動くプログラムでなくファイルができる、という静的配信の仕組み自体は なぜ速くて、落ちなくて、狙われにくいのか ― ケイズのCMSの静的配信 で説明しています。
(事前圧縮ファイルが無くても問題ありません。その場合は配信側が動的圧縮にフォールバックするだけ ―― 事前圧縮は「あれば効く」上乗せです。)
何を圧縮し、何をしないか
圧縮が効くのは テキスト系です。HTML・CSS・JavaScript・JSON・XML・SVG・テキストは繰り返しの多い構造なので大きく縮みます。一方、画像(PNG・JPEG・WebP)・PDF・動画はすでに圧縮済み。これらをもう一度圧縮しても、ほとんど縮まないうえに CPU だけ無駄に食います。だから事前圧縮の対象は テキスト系だけ に絞ります。
大物の画像や PDF は、前回の「一意な URL + 長期キャッシュ」で軽くする側 ―― 圧縮ではなくキャッシュで効かせます。
効き目は構成しだい ― CDN なし/あり
事前圧縮が本当に効くかは、配信構成で分かれます。
- CDN を前に置かない(静的サーバ直配信) ―― ここが事前圧縮の本領です。オリジンの静的サーバが、クライアントの対応に応じて 作り置きの圧縮ファイルをそのまま返します(その場で圧縮しない。gzip はそのまま、brotli 対応の配信なら最高品質の
.brを)。全アクセスをオリジンが捌くからこそ、毎回の圧縮を消せる効果が大きい。Vary: Accept-Encodingで圧縮版・非圧縮版の取り違えを防ぎます。 - CDN を前に置く ―― エッジが 初回に 1 回だけ圧縮してキャッシュします。以降はエッジが圧縮済みを返すので、オリジンの事前圧縮ファイルの出番は限定的。むしろ重複になるので、デプロイからは
.gz/.brを除外します。
cms-core は どちらの構成も想定して、公開時に圧縮版を用意します。CDN を挟む運用ではエッジに任せ、挟まない運用では作り置きが効く ―― 同じ生成物が、構成に応じて使い分けられます。
圧縮しても ETag は変わらない ― 転送と再検証は別の層
前回までと混ざらないように整理します。圧縮は「転送の形」を変えるだけで、コンテンツが変わったかどうか(ETag が見ているもの)とは別の話です。だから前々回の 安定した ETag と 304 はそのまま効き、前回の キャッシュバスティング も独立して働きます。Vary: Accept-Encoding は「同じ URL でも encoding が違えば別の表現」とキャッシュに教える仕上げです。3 つは互いに干渉せず、重ねて効かせられます。
ここで「圧縮した結果から ETag を計算するなら、結局リクエスト毎に圧縮することになるのでは?」と思うかもしれません。そうはなりません。前々回のとおり ETag は ファイルの更新時刻とサイズ から作られ、中身を圧縮してみる必要はありません。ETag は圧縮より前の段階で確定しており、変わっていなければ 304 で 本体を送らない=そもそも圧縮も起きません。圧縮の CPU がかかるのは「実際に本体を送る(200)」ときだけで、事前圧縮はまさにそこを肩代わりします。
(逆に、もし ETag を 圧縮後データのハッシュ で作る設計だと、ETag を出すためだけに毎回圧縮が必要になり、304 で済むはずの場面でも CPU を食います ―― 更新時刻+サイズの ETag が効いてくる理由です。)
配信 3 部作のまとめ ― 重い仕事は「公開時に 1 回」
3 回かけて見てきた「配信を軽くする」3 つの層が出そろいました。
- ETag(CDN と ETag を入れたのにサーバ負荷が上がった ― 動的生成と CDN はなぜ相性が悪いのか)―― 変わっていなければ 304 で本体を送らない(再検証を安く)。
- キャッシュバスティング(更新したのに古い CSS が表示される ― キャッシュバスティングを content hash で正しくやる)―― 中身が変わったら URL を変え、変えた分だけ失効させる。
- 事前圧縮(今回)―― 公開時に 1 回だけ圧縮しておく(とくに brotli 最高品質や CDN なし運用で効く)。
共通点は 「重い仕事を、利用者の来訪時ではなく公開時に 1 回だけ済ませる」 こと。前 2 つは効果がはっきりしていて、3 つ目は構成しだい ―― それでも「作っておけば、CDN 有りでも無しでも配信が軽い」という意味で、同じ設計思想の上にあります。ケイズの CMS は、公開(静的生成)のたびにこれらを自動で行います。
圧縮自体は安く(とくに gzip)、事前圧縮が効くのは「brotli を最高品質で使う」「CDN を挟まない」構成です。CDN を前に置くならエッジが圧縮するので出番は限定的。cms-core は両方を想定し、公開時に圧縮版を用意しておくので、構成が変わっても配信は軽いままです。
このブログは CDN(エッジ)配信なので、実際の圧縮はエッジが行っています。一方、CDN を挟まずに静的サーバだけで配る運用なら、同じ作り置きがそのまま効きます ―― 「公開時に 1 回」用意しておけば、構成が変わっても配信は軽いまま。それが、3 部作に共通する考え方です。配信が「なぜ速くて、落ちなくて、狙われにくいのか」の全体像は なぜ速くて、落ちなくて、狙われにくいのか ― ケイズのCMSの静的配信 にまとめています。

