gazou-compressor.jp

配信とキャッシュ設計:Cache-Control / ETag / SWR の最短ガイド(Next.js)

速さと整合性はCache-Control の設計で決まります。静的は immutable、動的は s-maxagestale-while-revalidate、ユーザー固有は no-store。 Next.js の headers() / fetch() / revalidate を組み合わせれば、CDN配信で速いのに壊れない運用ができます。

先に結論(運用ルール)
  • 画像・フォント等の静的アセット:public, max-age=31536000, immutable
  • HTML/APIの動的キャッシュ:max-age=0, s-maxage=3600, stale-while-revalidate=86400
  • ユーザー固有/機微:private, no-store(クッキー依存・個人化)
アセットの書き出しや軽量化は /compressor /compare、ZIP配布の設計は 画像アセットのZIP配布UX も参照。

要点(TL;DR)

1. なぜキャッシュ設計が効くか

速度指標(LCP/INP/TTFB)は配信レイヤの影響が大きいです。immutable でアセットを確実にCDN/ブラウザへ定着させ、 HTML は s-maxage + SWR で“早く返しつつ裏で更新”。破壊的変更はファイル名のコンテンツハッシュで担保します。

2. 最短フロー(実務)

  1. アセットを immutablepublic/ 配下や画像/フォントにmax-age=31536000, immutable を付与。
  2. HTML/API は CDN 優先max-age=0, s-maxage=3600, stale-while-revalidate=86400 を付与。
  3. データ再検証fetchnext.revalidate を設定(例:60秒)。
  4. 個人化は no-store:クッキー/セッション依存のレスポンスはキャッシュ禁止。

3. 実装レシピ(コピペOK)

3.1 next.config.js の headers()

JavaScript
// next.config.js(ESM)
/** @type {import('next').NextConfig} */
const nextConfig = {
  async headers() {
    return [
      // 1) public/ の画像・フォントなど“ほぼ不変”の静的アセット
      {
        source: "/:path*\\.(avif|webp|png|jpg|jpeg|gif|svg|ico|mp4|webm|woff2)",
        headers: [
          { key: "Cache-Control", value: "public, max-age=31536000, immutable" },
        ],
      },
      // 2) API(ユーザー固有レスポンスなど)は都度生成
      {
        source: "/api/:path*",
        headers: [{ key: "Cache-Control", value: "private, no-store" }],
      },
      // 3) HTML は CDN 優先(オリジンは常に最新) + SWR
      {
        source: "/articles/:slug",
        headers: [
          { key: "Cache-Control", value: "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400" },
        ],
      },
    ];
  },
};
export default nextConfig;

3.2 fetch の revalidate 設計

TypeScript
// サーバーコンポーネント/Route Handler 内のデータ取得
// ISR相当:最長60秒で自動再検証(CDNはs-maxageに従う)
const res = await fetch("https://example.com/data.json", {
  next: { revalidate: 60 }, // 0で常新, 60で最大1分再利用
  // cache: "force-cache" | "no-store" | "reload"(基本は指定不要でOK)
});
const data = await res.json();

3.3 Route Handler のキャッシュ制御

TypeScript
// app/api/news/route.ts(サンプル)
// CDNは1時間キャッシュ、バックグラウンドで1日までSWR
export const revalidate = 3600; // fetch()の既定再検証秒
export async function GET() {
  const res = await fetch("https://example.com/news.json", { next: { revalidate } });
  const body = await res.text();
  return new Response(body, {
    headers: {
      "Content-Type": "application/json; charset=utf-8",
      "Cache-Control": "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400",
      // ETagは Response が自動付与する環境もあります(CDN/プラットフォーム依存)
    },
  });
}

4. 応用と使いどころ

5. 公開前チェック

6. まとめ

静的は immutable、動的は s-maxage + SWR、個人化は no-store。 Next.js の仕組みを活かせば、配信はシンプルに回せます。画像そのものの品質は /compressor で最適化して、配信と合わせて総合最適にしましょう。

7. FAQ

immutable と ETag はどちらを使う?
どちらも併用できます。immutable は“そもそも取りに行かない”指示、ETag は取りに行って差分のみ返す仕組みです。 変更時はファイル名をハッシュ化してURLを変えるのが鉄則です。
max-age と s-maxage の違いは?
max-age はブラウザ、s-maxage はCDN(共有キャッシュ)向けです。HTMLは max-age=0, s-maxage=… とすることで、ブラウザは再取得しつつCDNは高速に返せます。

FAQ(よくある質問)

画像形式の基本方針は?(写真/スクショ/透過)
写真は AVIF / WebP(画質80–85%目安)、UIやスクショはPNG / WebP Lossless、単色ロゴはSVGが基本です。 実装の詳細は srcset/sizes設計ガイド スクショ最適化 を参照してください。
圧縮しても画質を落とさないコツは?
実表示幅に合わせたリサイズ → 過大ダウンロードを防ぎ、srcset/sizes を 実描画幅に一致させます。画質は写真で 80–85% を起点に、ノイズやエッジを目視確認。 仕上げは /compare で Before/After を見比べるのがおすすめです。
CLSを悪化させない画像の置き方は?
すべての画像に width/height(または親に aspect-ratio)を与え、広告・埋め込みは 予約サイズを先に確保します。詳しくは CLSゼロ設計ガイド を参照。

公開:2025-08-31

gazou-compressor.jp 編集部

画像圧縮・変換・背景除去などの実践テクニックと、Webで“速く・軽く・崩さない”ためのノウハウを発信しています。

関連記事