Brotli vs Gzip 圧縮比較|nginx静的・動的設定とCDN実践ガイド

(更新: 2026年6月21日 ) nginx Brotli Gzip 圧縮 パフォーマンス CDN Web
結論
  • テキストアセットの転送量削減では BrotliがGzipより15〜25%小さいのが2026年時点の一般的な計測値だ。
  • ただし動的Brotli高レベルはCPUとTTFBを悪化させる。
  • ビルド時に .br / .gz を生成し、nginxは brotli_static on; gzip_static on; で配信するのが最も安全で速い。
  • CDNがエッジ圧縮を担う場合も、オリジン静的圧縮とHTTP/2・HTTP/3の多重化CRP最適化を組み合わせて初めてLCPが動く。

はじめに — 圧縮はまだ効くのか

2026年のフロントエンドはバンドル分割・Tree Shaking・ESM配信が当たり前になり、単一の巨大 app.js は減った。それでも典型的なSPA初回ロードでは HTML + CSS + JS合計 400〜900KB(圧縮前) が残り、モバイル回線(下行 5〜20Mbps、RTT 80〜150ms)では転送バイトがそのままLCPに効く。

クリティカルレンダリングパス最適化でレンダーブロッキングを削っても、ネットワーク上のバイト数が大きければWaterfallは長いままだ。HTTP/2・HTTP/3が接続効率を改善しても、圧縮は「1リクエストあたりのペイロード」を直接小さくする。両者は競合せず、積み重ねる。

本記事では次を実務レベルで扱う。

  1. BrotliとGzipのアルゴリズム差とブラウザ対応
  2. 静的圧縮(precompressed)動的圧縮(on-the-fly) のnginx設定
  3. compression levelのCPU・サイズトレードオフ
  4. CDN(Cloudflare等)との役割分担
  5. 2026年ベンチマーク数値と検証コマンド

BrotliとGzip — 技術的な違い

項目GzipBrotli
標準化RFC 1952(1996)RFC 7932(2016)
辞書固定ハフマン、32KBウィンドウコンテキストモデリング + 静的辞書(約120KB)
典型圧縮率(テキスト)ベースライン同条件で 15〜25% 小さい ことが多い
エンコードCPU比較的軽いレベルが上がると急増
デコードCPU軽い軽い(gzip同等〜やや速い)
ブラウザ対応ほぼ100%Chrome/Firefox/Safari/Edge 全対応(2020年以降)
nginx標準同梱別モジュール(ngx_brotli)

Brotliの強みは 事前定義された120KB超の静的辞書 にある。HTML・CSS・JSに頻出するトークン(function, class, div, https:// など)が辞書に入っているため、短いファイルほど相対的な削減率が大きくなる。Gzipは汎用DEFLATEで、辞書なしでも安定するが、テキストに対してはBrotliに劣る。

Content-Encoding のネゴシエーション

ブラウザはリクエストヘッダーで対応コーデックを宣言する。

GET /assets/app.js HTTP/2
Host: example.com
Accept-Encoding: br, gzip, deflate, zstd

サーバーはレスポンスで実際のエンコーディングを返す。

HTTP/2 200 OK
Content-Type: application/javascript
Content-Encoding: br
Content-Length: 84231
Vary: Accept-Encoding
Cache-Control: public, max-age=31536000, immutable

Vary: Accept-Encoding はCDNキャッシュで必須だ。これがないと、gzipでキャッシュされたレスポンスがbr対応クライアントに誤配信される。圧縮とキャッシュはセットで設計する。

zstdについて

2025〜2026年にかけて一部CDN・originで zstd 圧縮が実験導入されている。Brotliより高速にエンコードでき、圧縮率はBrotliに近い場合がある。ただしブラウザネイティブ対応は限定的で、2026年6月時点では Brotli + Gzip のデュアルスタック が本番の無難な選択肢だ。nginxのzstdモジュールはngx_http_zstd_module等で別途ビルドが必要。

2026年ベンチマーク — 数値で見る削減率

以下は同一ビルド成果物(Vite + React 19、本番 minify 済み)を 2026年6月 に計測した代表値だ。環境は AMD EPYC 7R13(4 vCPU)、nginx 1.26 + ngx_brotli 1.0.0。再現性のためファイル単体で計測している。

単一ファイル比較(bytes)

ファイル生サイズgzip -6brotli -6brotli -11Brotli-6 vs gzip-6
index.html4,8121,6431,2911,198-21.4%
app.[hash].js287,42089,10472,31868,901-18.8%
vendor.[hash].js412,880128,550101,20496,882-21.3%
app.[hash].css48,2209,8827,1046,550-28.1%
sprite.svg22,1006,4404,8924,501-24.0%
合計775,432235,619186,809177,032-20.7%

モバイル4G相当(下行 10Mbps、RTT 100ms)で転送時間だけを概算すると、gzip合計 235KB ≈ 188ms、brotli-6合計 187KB ≈ 150ms約38ms短縮。LCP要素がJS/CSSに依存するSPAでは、CRP最適化と合わせて数百ms改善に寄与しうる。

動的圧縮のTTFBペナルティ(同一4 vCPU)

方式287KB JS 初回圧縮TTFB増分(p50)
非圧縮配信0 ms0 ms(基準)
gzip 動的 level 612 ms+12 ms
brotli 動的 level 648 ms+48 ms
brotli 動的 level 11890 ms+890 ms
静的 .br 配信(事前圧縮)0 ms(配信のみ)+1 ms(ディスクI/O)

動的brotli-11は 1リクエストあほぼ1秒のCPU を食い、並行リクエストでスループットが崩れる。静的precompressedならエンコードコストはCIに閉じ込められる。

775
生データ: 775
生データ
236
gzip -6: 236
gzip -6
187
brotli -6: 187
brotli -6
177
brotli -11: 177
brotli -11

静的圧縮 vs 動的圧縮 — 使い分け

方式いつ圧縮するCPU負荷の場所向いているリソース
静的(precompressed)CI/CDビルド時デプロイパイプラインJS/CSS/HTML/JSON/SVG(ハッシュ付きファイル名)
動的(on-the-fly)リクエスト毎nginxワーカーCPUSSR HTML、API JSON、動的生成テキスト
CDNエッジ圧縮キャッシュミス時CDN PoPオリジン非圧縮・小規模サイト

鉄則: ファイル名にコンテンツハッシュがあるアセット(app.a1b2c3.js)はビルド時に .br.gz を生成し、nginxは brotli_static / gzip_static で配信する。SSRのHTMLや /api/ JSONだけ動的圧縮に残す。

ビルドパイプライン例(npm scripts)

{
  "scripts": {
    "build": "vite build",
    "compress": "node scripts/compress-assets.mjs",
    "build:prod": "npm run build && npm run compress"
  }
}
// scripts/compress-assets.mjs
import { readdir, readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { gzipSync } from 'node:zlib';
import { brotliCompressSync, constants } from 'node:zlib';

const DIST = './dist';
const TEXT_EXT = new Set(['.html', '.js', '.css', '.svg', '.json', '.txt', '.xml']);

async function walk(dir) {
  const entries = await readdir(dir, { withFileTypes: true });
  for (const ent of entries) {
    const full = join(dir, ent.name);
    if (ent.isDirectory()) await walk(full);
    else if (TEXT_EXT.has(ent.name.slice(ent.name.lastIndexOf('.')))) {
      const buf = await readFile(full);
      await writeFile(full + '.gz', gzipSync(buf, { level: 6 }));
      await writeFile(
        full + '.br',
        brotliCompressSync(buf, {
          params: { [constants.BROTLI_PARAM_QUALITY]: 11 },
        })
      );
      console.log('compressed:', full);
    }
  }
}

await walk(DIST);

Viteなら vite-plugin-compression を使う手もある。いずれにせよ デプロイ成果物に .br / .gz が並ぶ 状態を作る。

nginx設定 ① — 基本のGzip(動的)

標準nginxでそのまま使える最小構成。まずはこれで現状把握し、のちに静的圧縮へ移行する。

# /etc/nginx/conf.d/gzip-basic.conf
# 動的Gzipのみ — 検証・小規模サイト向け

gzip on;
gzip_comp_level 6;
gzip_min_length 256;
gzip_vary on;
gzip_proxied any;
gzip_disable "msie6";

gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/javascript
    application/x-javascript
    application/json
    application/xml
    application/xml+rss
    application/atom+xml
    image/svg+xml
    font/ttf
    font/otf
    application/vnd.ms-fontobject;

# HTMLはデフォルトで gzip_types に含まれないため明示
gzip_types text/html;

gzip_min_length 256 は小さすぎるレスポンスの圧縮を避ける。256バイト未満はヘッダー分だけ肥大化することがある。gzip_vary on はCDN利用時に必須。

nginx設定 ② — Brotli動的圧縮(ngx_brotli)

ngx_brotliモジュールをロードしたうえでの動的Brotli設定。本番ではレベル4〜6に抑える。

# /etc/nginx/conf.d/brotli-dynamic.conf
# ngx_brotli モジュール必須(load_module または --add-module ビルド)

load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

brotli on;
brotli_comp_level 5;
brotli_min_length 256;
brotli_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/javascript
    application/x-javascript
    application/json
    application/xml
    application/xml+rss
    application/atom+xml
    image/svg+xml
    font/ttf
    font/otf
    application/vnd.ms-fontobject
    text/html;

Debian/Ubuntuでカスタムビルドした場合、modules-enabled にシンボリックリンクを置く。Dockerなら多段ビルドでnginx+brotliイメージを作る。

FROM debian:bookworm-slim AS builder
RUN apt-get update && apt-get install -y build-essential libpcre3-dev zlib1g-dev libssl-dev git
RUN git clone --recursive https://github.com/google/ngx_brotli.git /tmp/ngx_brotli
RUN curl -O http://nginx.org/download/nginx-1.26.2.tar.gz && tar xf nginx-1.26.2.tar.gz
WORKDIR /nginx-1.26.2
RUN ./configure \
    --with-compat \
    --add-dynamic-module=/tmp/ngx_brotli \
    && make modules

FROM nginx:1.26-bookworm
COPY --from=builder /nginx-1.26.2/objs/ngx_http_brotli_filter_module.so /etc/nginx/modules/
COPY --from=builder /nginx-1.26.2/objs/ngx_http_brotli_static_module.so /etc/nginx/modules/

nginx設定 ③ — 静的圧縮(本番推奨)

app.js と同階層に app.js.br / app.js.gz があるとき、クライアントの Accept-Encoding に応じて自動選択する。エンコードCPUゼロが最大の利点。

# /etc/nginx/conf.d/compression-static.conf
# 事前圧縮ファイル .br / .gz を優先配信

load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

gzip_static on;
brotli_static on;

# 静的ファイルに .br/.gz が無い場合のフォールバック(動的)
gzip on;
gzip_comp_level 5;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/css application/javascript application/json image/svg+xml text/html;

brotli on;
brotli_comp_level 4;
brotli_min_length 1024;
brotli_types text/css application/javascript application/json image/svg+xml text/html;

server {
    listen 443 ssl http2;
    server_name cdn-origin.example.com;

    root /var/www/app/dist;
    index index.html;

    location /assets/ {
        expires 1y;
        add_header Cache-Control "public, max-age=31536000, immutable";
        # brotli_static / gzip_static が .br / .gz を自動選択
        try_files $uri =404;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}

brotli_static onAccept-Encoding: br のとき file.br を探し、あれば Content-Encoding: br で返す。無ければ通常ファイルまたは動的brotliにフォールバックする。

nginx設定 ④ — 静的アセットと動的APIの分離

大規模構成では location ごとに圧縮方針を分ける/assets/ は静的のみ、/api/ は動的JSON、/ はSSR。

# /etc/nginx/sites-available/app-production.conf
# 静的 precompressed + API動的圧縮のハイブリッド

load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

map $http_accept_encoding $css_js_encoding {
    default          "";
    "~*br"           ".br";
    "~*gzip"         ".gz";
    "~*gzip.*br"     ".br";
}

upstream app_backend {
    server 127.0.0.1:3000;
    keepalive 32;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    root /var/www/app/dist;

    # --- 静的アセット: 事前圧縮のみ、動的圧縮オフ ---
    location ^~ /assets/ {
        gzip off;
        brotli off;
        gzip_static on;
        brotli_static on;

        expires 1y;
        add_header Cache-Control "public, max-age=31536000, immutable";
        add_header Vary "Accept-Encoding" always;
        access_log off;
        try_files $uri =404;
    }

    # --- API: 動的JSON圧縮 ---
    location ^~ /api/ {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        gzip on;
        gzip_comp_level 5;
        gzip_types application/json;
        gzip_vary on;

        brotli on;
        brotli_comp_level 4;
        brotli_types application/json;

        add_header Vary "Accept-Encoding" always;
    }

    # --- SSR / HTML: 中程度の動的圧縮 ---
    location / {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;

        gzip on;
        gzip_comp_level 6;
        gzip_types text/html text/css application/javascript;
        gzip_vary on;

        brotli on;
        brotli_comp_level 5;
        brotli_types text/html text/css application/javascript;

        add_header Vary "Accept-Encoding" always;
    }
}

/assets/gzip off; brotli off; としているのは、誤って動的圧縮に落ちた場合のCPU浪費を防ぐため。.br が無いファイルは生のまま返り、デプロイチェックで検知する運用が望ましい。

nginx設定 ⑤ — CDNオリジン向け(Cloudflare連携)

CloudflareはデフォルトでエッジBrotli/Gzipを行う。オリジンで二重圧縮しないよう注意する。オリジンがすでに Content-Encoding: br で返す場合、CFは再圧縮しないが、キャッシュキーと Vary の整合が重要。

# /etc/nginx/sites-available/origin-behind-cloudflare.conf
# Cloudflare オリジン: 静的 br/gz 配信 + 実IP復元

load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

# Cloudflare IPレンジからの接続のみ許可(本番推奨)
# set_real_ip_from は CF 公開IPリストを include

real_ip_header CF-Connecting-IP;
real_ip_recursive on;

gzip_static on;
brotli_static on;
gzip_vary on;

server {
    listen 443 ssl http2;
    server_name origin.example.com;

    root /var/www/releases/current;

    # オリジンは圧縮済みバイトを返す — CFはそのままキャッシュ
    location / {
        brotli off;
        gzip off;
        gzip_static on;
        brotli_static on;

        add_header Cache-Control "public, max-age=0, s-maxage=86400" always;
        add_header Vary "Accept-Encoding" always;
        add_header X-Origin-Compression "static-br-gz" always;

        try_files $uri $uri/ /index.html;
    }

    location /health {
        access_log off;
        return 200 "ok\n";
        add_header Content-Type text/plain;
    }
}

Cloudflareダッシュボード側では Speed → Optimization → Brotli: ONAuto Minify はオフ(ビルド済みと競合するため)。s-maxage でエッジTTLを制御し、オリジンは max-age=0 でブラウザ直キャッシュを短くするパターンが多い。

nginx設定 ⑥ — 圧縮効果のログ計測

どのコーデックで何バイト返したかをログに残し、週次で集計する。

# /etc/nginx/conf.d/compression-logging.conf

log_format compression '$remote_addr - [$time_local] '
                       '"$request" $status $body_bytes_sent '
                       'enc=$sent_http_content_encoding '
                       'ct=$sent_http_content_type '
                       'rt=$request_time '
                       'upstream=$upstream_addr';

server {
    listen 443 ssl http2;
    server_name example.com;

    access_log /var/log/nginx/access.log compression;

    # 以下 server ブロックの設定は他ファイルと merge される想定
}

enc=br の割合が90%未満なら、古いクライアント・ボット・設定ミスを疑う。enc=- が多いのは画像やwoff2など非圧縮typesで正常な場合もある。

compression level — トレードオフの深掘り

Gzipレベル(1〜9)

レベルエンコード速度サイズ(287KB JS)推奨用途
1最速98,200 bytes超高トラフィック動的
4速い90,800 bytes動的API
6標準89,104 bytes動的汎用デフォルト
9遅い87,950 bytes静的生成のみ

レベル6→9の差は 約1.3% しか縮まないのにCPUは2〜3倍。動的では6で十分。

Brotliレベル(0〜11)

レベルエンコード速度サイズ(287KB JS)推奨用途
4速い76,100 bytes高負荷動的
5〜672,318 bytes動的上限
9遅い70,102 bytesCI静的生成
11最遅68,901 bytesビルド時 .br

動的は 5以下、静的 .br11 が一般的。レベル11は1ファイルあたり数百msかかっても、数千ファイルを並列圧縮すればCI 2〜5分で収まる。

シナリオ 推奨設定
ハッシュ付きJS/CSS ビルド時 brotli-11 + gzip-6 → brotli_static / gzip_static
SSR HTML 動的 brotli-5 / gzip-6、またはキャッシュ+事前生成
REST JSON API 動的 brotli-4 / gzip-5、100KB超はページネーション検討
WebSocket 圧縮なし(permessage-deflateは別途検討)
既に圧縮済み woff2 / webp / avif / mp4 は types から除外

CDNとの役割分担 — 3パターン

パターンA: CDNエッジ圧縮のみ(小規模・レガシー)

  • オリジン: 非圧縮
  • CDN: 初回ミス時に圧縮しキャッシュ
  • メリット: オリジン設定が単純
  • デメリット: オリジン↔CDN間の転送が太い、ミス時TTFB悪化

パターンB: オリジン静的圧縮 + CDNパススルー(推奨)

  • オリジン: .br / .gzContent-Encoding 付きで返す
  • CDN: 圧縮済みをキャッシュ、再圧縮しない
  • メリット: エンドツーエンドで最小バイト、ミス時も速い
  • デメリット: ビルドパイプラインに圧縮ステップが必要

パターンC: オリジン動的 + CDN再圧縮(非推奨)

  • オリジンがgzip、CDNがbrotliに変換など
  • CPU二重消費、キャッシュキー複雑化。避ける

HTTP/2・HTTP/3の多重化と組み合わせると、圧縮後の小さなフレームが多数並列で飛ぶ。転送バイト削減は 単一コネクションの占有時間短縮 に直結する。

検証手順 — curlとDevTools

Accept-Encoding ごとのサイズ比較

# 非圧縮(Accept-Encoding なし)
curl -sI -H "Accept-Encoding: identity" https://example.com/assets/app.js | grep -i content-length

# gzip
curl -sI -H "Accept-Encoding: gzip" https://example.com/assets/app.js | grep -E 'content-encoding|content-length'

# brotli
curl -sI -H "Accept-Encoding: br" https://example.com/assets/app.js | grep -E 'content-encoding|content-length'

# 実バイト数計測
curl -s -H "Accept-Encoding: br" https://example.com/assets/app.js | wc -c

デコード後の整合性

curl -s -H "Accept-Encoding: br" https://example.com/assets/app.js | brotli -d | shasum -a 256
curl -s https://example.com/assets/app.js | shasum -a 256
# ハッシュが一致すれば静的/動的どちらでも中身は同一

Lighthouse / WebPageTest

  1. WebPageTest — Compare Viewで「圧縮OFF(identity)」は再現困難なため、Before/Afterで設定変更前後を比較
  2. Chrome DevTools Network — Size列の「transferred」vs「resource」で圧縮率を確認
  3. Lighthouse — 「Enable text compression」監査が赤なら動的圧縮漏れ
圧縮だけではLCPは直らない場合がある

転送量20%削減しても、LCP要素が未最適化の画像1枚(500KB)なら効果は薄い。圧縮は テキストアセット に効く。CRP最適化でLCP要素の特定・preload、画像はWebP/AVIFへ。プロトコル・圧縮・レンダリングの順でボトルネックを切り分ける。

よくある障害と対処

症状原因対処
ブラウザで文字化け二重圧縮、壊れた .brcurl --compressed で検証、CIでハッシュ比較
CDNでgzip/brが交互に混在Vary: Accept-Encoding 欠落nginx gzip_vary on / add_header Vary
.br があるのにgzipが返るbrotli_static 未ロードload_modulebrotli_static on 確認
CPU 100%動的brotli-11を全レスポンスに適用静的化またはレベル引き下げ
ETagが効かない圧縮版と非圧縮版でETag不一致gzip_static は元ファイルのmtime/etagを使用
APIが遅い大きなJSONを毎回brotli-6ページネーション、レベル4、キャッシュ

IE11・レガシー対応

2026年にIE11サポートはほぼ終了しているが、社内ツールで必要なら gzip_static だけ有効にし、brotliはモダンブラウザ向けにする。gzip_disable はMSIE用の遺物で、現行Edgeは不要。

ngx_brotli のビルド手順(ソースコンパイル)

パッケージマネージャにBrotli付きnginxが無い場合、ソースからモジュールを追加する。本番前にステージングで nginx -t と負荷試験を行うこと。

# Ubuntu 22.04/24.04 向け — 依存パッケージ
sudo apt-get update
sudo apt-get install -y build-essential libpcre3-dev zlib1g-dev libssl-dev git curl

# nginx 本体と ngx_brotli の取得
NGINX_VERSION=1.26.2
curl -O https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
tar xf nginx-${NGINX_VERSION}.tar.gz
git clone --recursive https://github.com/google/ngx_brotli.git

cd nginx-${NGINX_VERSION}
./configure \
  --prefix=/etc/nginx \
  --sbin-path=/usr/sbin/nginx \
  --conf-path=/etc/nginx/nginx.conf \
  --error-log-path=/var/log/nginx/error.log \
  --http-log-path=/var/log/nginx/access.log \
  --pid-path=/var/run/nginx.pid \
  --with-http_ssl_module \
  --with-http_v2_module \
  --with-http_gzip_static_module \
  --with-compat \
  --add-dynamic-module=../ngx_brotli

make modules
sudo cp objs/ngx_http_brotli_filter_module.so /etc/nginx/modules/
sudo cp objs/ngx_http_brotli_static_module.so /etc/nginx/modules/

/etc/nginx/nginx.conf の先頭付近に load_module を追記する。

load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

user www-data;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 4096;
    multi_accept on;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    sendfile      on;
    tcp_nopush    on;
    keepalive_timeout 65;
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

nginx -t && systemctl reload nginx で反映する。nginx -V 2>&1 | grep brotli でモジュールがロードされていることを確認する。

worker数と圧縮のCPU関係

動的圧縮は CPUバウンド だ。worker_processes auto; はコア数に合わせるのが基本だが、動的brotliを広く使う場合は worker_cpu_affinity でコア固定し、他の処理との競合を減らす手もある。静的precompressed主体なら worker_connections を増やしてもI/O待ちが主で、圧縮によるCPUスパイクは起きにくい。

Cloudflare / Fastly — ダッシュボード設定の要点

設定項目Cloudflare推奨値
BrotliSpeed → OptimizationON
Early HintsSpeed → OptimizationON(CRPと相性良)
Auto Minify (JS/CSS/HTML)Speed → OptimizationOFF(ビルド済みと競合)
Rocket LoaderSpeed → Optimization状況次第(JS実行順を変える)
Cache RulesRules/assets/* は Edge TTL 1年

オリジンが Content-Encoding: br で返す場合、Cloudflareは 既に圧縮されたレスポンスをそのままキャッシュ する。オリジンが非圧縮で Cache-Control のみ付与している場合、CFが初回ミス時にエッジで圧縮する。パターンB(オリジン静的圧縮) の方がミス時のTTFBが安定する。

レスポンスヘッダーで cf-cache-status: HIT かつ content-encoding: br が揃っているか、本番リリース後に必ずスポットチェックする。

セキュリティと圧縮 — BREACH / CRIME の注意

HTTPS上の圧縮は理論上 BREACH(HTTPレスポボディ)や CRIME(TLSレベル)攻撃の対象になり得る。秘密トークンを反射するAPIレスポンスを動的圧縮し、攻撃者が長さを計測できる条件は稀だが、以下を守る。

  • セッションID・CSRFトークンをURLクエリに載せない
  • 機密フィールドはレスポンスから除外
  • 極秘データは Cache-Control: no-store + 圧縮オフも検討

一般的な静的JS/CSS圧縮でのリスクは低い。問題になるのは ユーザー入力がエコーされる動的HTML だ。

本番導入チェックリスト

1

curlで主要アセットの Content-Encoding と転送バイト数のベースラインを記録する

2

CIに brotli-11 / gzip-6 の事前圧縮ステップを追加し .br / .gz をデプロイする

3

nginxで gzip_static / brotli_static を有効化し /assets/ の動的圧縮をオフにする

4

Vary: Accept-Encoding と Cache-Control をCDN設定と整合させる

5

動的圧縮は brotli-4〜5 / gzip-5〜6 に制限し TTFB を監視する

6

Lighthouse と WebPageTest で転送サイズ・LCP を Before/After 比較する

7

[HTTP/2・HTTP/3](/blog/http2-http3-違い-パフォーマンス比較/) が有効かつ圧縮と併用できているか確認する

関連記事

この記事は Webパフォーマンス テーマの一環です。あわせて読むと理解が深まる関連記事をまとめました。

トピック記事
Speculation Rules API 完全ガイドSpeculation Rules API 完全ガイド|prefetch・prerender・Chrome 144 prerender_until_script
fetchpriority属性でLCP画像を最適化するfetchpriority属性でLCP画像を最適化する|2026年実践ガイド
Web Vitals INP改善ガイドWeb Vitals INP改善ガイド|scheduler.yield・Long Task・Input Delay対策(2026)
content-visibility: auto 完全ガイドcontent-visibility: auto 完全ガイド|遅延レンダリングと長尺ページ最適化 2026
Server-Timing API 完全ガイドServer-Timing API 完全ガイド|Express ミドルウェアと Chrome DevTools 連携
CSS/JSを圧縮(Minify)するメリットCSS/JSを圧縮(Minify)するメリット|サイト高速化の基本
Service Worker キャッシュ戦略ガイドService Worker キャッシュ戦略ガイド|PWA の cache-first・network-first・SWR・Workbox 設計
View Transitions API 完全ガイドView Transitions API 完全ガイド|SPA・MPA ページ遷移と Astro/React 統合
WebP変換で画質が落ちる原因と最適な設定WebP変換で画質が落ちる原因と最適な設定
画像圧縮とWebP変換画像圧縮とWebP変換|PageSpeed改善の基本
APIキャッシュ設計ガイドAPIキャッシュ設計ガイド|ETag・Cache-Control・304・CDNキャッシュキーの実務
GraphQL N+1 問題の解決GraphQL N+1 問題の解決|DataLoader バッチング実装と Apollo Server 完全ガイド

まとめ

BrotliはGzipより テキストで15〜25%小さく なり、デコードコストは同等。弱点はエンコードの重さで、動的brotli高レベルはTTFBを壊す

2026年のベストプラクティスは次のとおり。

  1. ビルド時.br(level 11)と .gz(level 6)を生成
  2. nginxbrotli_static on; gzip_static on; を使い静的配信
  3. SSR/APIだけ動的圧縮(brotli 4〜5、gzip 5〜6)
  4. CDNは圧縮済みをキャッシュ、二重圧縮を避ける
  5. 計測で効果確認 — 転送バイトとLCPをCRP最適化とセットで見る

圧縮は安価で効果が大きいが、万能ではない。ボトルネックが画像やサードパーティスクリプトなら、そちらを先に直す。テキストアセットが支配的なSPA・ドキュメントサイトでは、本記事の静的Brotli構成を入れるだけで 初回ロード数十〜数百ms の改善が見込める。

次のアクション: 本番ドメインで curl -sI -H 'Accept-Encoding: br' https://your-domain/assets/ を実行し、content-encoding: br が返るか確認する。返らなければ .br ファイルのデプロイと brotli_static 設定から着手してほしい。