CDNエッジリサイズの落とし穴 2025 — アップスケール/キャッシュ/画質の三角形
公開: 2025年9月23日 · 読了目安: 5 分 · 著者: Unified Image Tools 編集部
はじめに
エッジでの画像リサイズやフォーマット変換は強力ですが、設計を誤ると「無限のバリアント」「キャッシュ断片化」「品質劣化」の三重苦に陥ります。本稿は、現場で頻出する落とし穴を体系化し、安全に運用するためのガードレールを提示します。
TL;DR
- アップスケール抑制と原寸以上の禁止をデフォルトに
- DPR/フォーマットの自動分岐はキャッシュキー設計とセットで
- 品質監視は差分/指標/目視の3点チェック
内部リンク: エッジ時代の画像配信最適化 CDN 設計 2025, 2025年のリサイズ設計 — レイアウトから逆算して 30–70% の無駄を削る
なぜ落とし穴が生まれるのか
- クエリ式 API(w, h, q, fmt, bg, fit...)が柔軟すぎて、バリアントが指数的に増殖
Accept
/DPR/言語/地域などのネゴシエーションでキャッシュキーが爆発- 元画像の解像度や色空間が一定でないため、変換時に画質/色の破綻が混入
回避には「入力(元画像)と出力(バリアント)を標準化し、作れるサイズ/フォーマットを限定」する思想が必要です。
典型アンチパターンと対策
- クエリを自由入力にして無制限の
w/h/q/fmt
を許可 → 許可リスト方式に変更(例: WIDTHS=[320,480,720,960,1280,1536]) - DPR=3 端末に合わせて幅×3 を無制限出力 → 実効表示幅×DPR を上限化(かつ原寸以上禁止)
Vary: *
的な拡張でキャッシュが断片化 →Vary: Accept
のみに絞り、DPR はクエリ/パスで管理- 自動フォーマット(AVIF/WebP/JP2)を優先しすぎ → 画質の下限(q/psy/シャープ)をガード、静止画/線画は別プリセット
- メタデータ維持の方針なし → 著作権/色空間/ICC プロファイル喪失。EXIF/ICC の保持/除去をスイッチで制御
キャッシュキー設計(安全な分割)
キーの構成例:
<origin-path>?w=<width>&fmt=<format>&dpr=<dpr>
- 必須:
w
は許可リストに丸め、dpr
は{1,2,3}
のいずれか fmt
はavif
/webp
/jpeg
/png
等の限定セットq
はサーバ側プリセット名(soft, photo, line, ui)で受け取り、数値は受けない
HTTP ヘッダー例:
Cache-Control: public, max-age=31536000, immutable
Vary: Accept
Content-Type: image/avif
Accept
による分岐は同じ URL に複数フォーマットをぶら下げられる利点がある一方、curl
等のデバッグで分かりにくくなります。可観測性のためにレスポンスヘッダーへ決定情報(X-Format
, X-Width
, X-DPR
)を出すと良いです。
エッジ実装(擬似コード)
Cloudflare Workers 風の疑似実装:
const WIDTHS = [320, 480, 720, 960, 1280, 1536];
function clampWidth(w) {
const n = Math.max(...WIDTHS.filter((x) => x <= w));
return n ?? WIDTHS[0];
}
export default {
async fetch(req) {
const url = new URL(req.url);
const accept = req.headers.get('Accept') || '';
const dpr = Math.min(3, Math.max(1, Number(url.searchParams.get('dpr') || 1)));
const desired = Number(url.searchParams.get('w') || 0);
const width = clampWidth(desired);
// 原寸以上禁止(例: originWidth はメタから取得)
const originWidth = await getOriginWidth(url.pathname);
const target = Math.min(width * dpr, originWidth);
const fmt = accept.includes('image/avif') ? 'avif'
: accept.includes('image/webp') ? 'webp'
: 'jpeg';
const res = await transformAtEdge({
path: url.pathname,
width: target,
format: fmt,
preset: choosePreset(url),
noUpscale: true,
});
return new Response(res.body, {
headers: {
'Content-Type': `image/${fmt}`,
'Cache-Control': 'public, max-age=31536000, immutable',
Vary: 'Accept',
'X-Width': String(target),
'X-Format': fmt,
'X-DPR': String(dpr),
},
});
},
};
ガードレール(運用ポリシー)
- 原寸超え禁止(withoutEnlargement)を常時オン。リクエストは最も近い許可幅に丸める
- 代表幅は 4〜6 段に限定、
sizes
設計と揃える(過剰ディメンションを作らない) - フォーマットは
avif→webp→jpeg
の順でフォールバック、一貫した色空間(sRGB)へ変換 q
の直指定は禁止。プリセット(photo/line/ui)で画質・シャープを包括設定- 変換失敗/タイムアウト時は原画像を返し、監視に上げる(静的フェイルオープン)
品質監視(差分 + 指標 + 目視)
- 差分: 代表 30 枚のゴールデンセットでビットマップ比較(SSIM/LPIPS)
- 指標: 転送量/デコード時間/LCP の p75 を定点観測(RUM)
- 目視: 線画/文字/グラデ/肌の 4 カテゴリで QC(
q
とsharpness
を調整)
サンプル(Node, sharp):
import sharp from 'sharp';
async function ssimLike(a, b) {
const [A, B] = await Promise.all([
sharp(a).resize(800).raw().toBuffer(),
sharp(b).resize(800).raw().toBuffer(),
]);
// ここに SSIM 近似ロジックを実装(省略)。閾値を 0.95 などに設定
}
ケーススタディ(短編)
事例1: 無制限クエリでキャッシュが壊滅
- 症状: ほぼ毎回ミス(MISS)。オリジン負荷が跳ね上がる
- 対策: 許可リストへ丸め、DPR は
{1,2}
のみ許容、q
はプリセット化 - 結果: ヒット率 20%→75%、LCP も 6% 改善
事例2: 高精細端末でのアップスケール劣化
- 症状: DPR=3 が原寸を超えてリクエスト、文字がにじむ
- 対策: 原寸超え禁止 + 表示幅×DPR で上限化
- 結果: 文字にじみ解消、INP のロングタスクも減少
FAQ
Q. 自動フォーマットは常に有効?
A. ほぼ有効ですが、ポスター化が目立つ線画/文字には JPEG(高 q + シャープ)を使うなどプリセットで分岐を。
Q. Vary: DPR
は付けるべき?
A. 推奨しません。URL(w, dpr)で明示し、Vary: Accept
に限定する方がキャッシュ予測性が上がります。
Q. 代表幅は何段が良い?
A. 多すぎると断片化します。4〜6 段が現実解。sizes
と揃えて全体最適化を。
チェックリスト(配信用)
- [ ]
w
は許可リストに丸め、原寸超え禁止 - [ ]
dpr
は{1,2,(3)}
まで、表示幅×DPR の上限を実装 - [ ]
fmt
は限定セット、Vary: Accept
のみ - [ ]
q
は数値で受けずプリセット名にマップ - [ ] sRGB 変換/ICC 処理の一貫性を担保
- [ ] ゴールデンセットで差分/目視の QC を定常運用
関連ツール
関連記事
画像最適化の基本 2025 — 勘に頼らない土台づくり
どのサイトにも効く、速くて美しい配信のための最新ベーシック。リサイズ→圧縮→レスポンシブ→キャッシュの順で安定運用に。
エッジ時代の画像配信最適化 CDN 設計 2025
エッジ/CDNでの画像配信を高速・安定・省トラフィックにする設計ガイド。キャッシュキー、Vary、Acceptネゴシエーション、Priority Hints、Early Hints、プリコネクトまで総合解説します。
画像のA/Bテスト設計 2025 — 画質・速度・CTRを同時に最適化
フォーマット/画質/サイズ/プレースホルダーの組み合わせを、LCP/INPとCTRで評価し、実運用に落とすテスト設計。
画像配信のCache-ControlとCDN無効化 2025 — 早く・壊さず・確実に刷新
immutable/short-max-age/stale-while-revalidate/バージョニング/ETag運用で、キャッシュ効率と確実な更新を両立する実装ガイド。
画像の画質評価指標 SSIM/PSNR/Butteraugli 実践ガイド 2025
機械的な数値指標をうまく活用して、圧縮やリサイズ後の画質を客観的に比較・検証するための実践手順をまとめます。SSIM/PSNR/Butteraugli の使い分けと注意点、ワークフローへの組み込み例まで。
2025年のリサイズ設計 — レイアウトから逆算して 30–70% の無駄を削る
レイアウトに基づく目標幅の導出、複数サイズの生成、srcset/sizes の実装まで。最も効く削減手法を体系化。