メインコンテンツへスキップ
友田 陽大
Vercel 本番運用
Vercel
Next.js
パフォーマンス
ISR
キャッシュ
コスト最適化
フロントエンド

Vercel キャッシュ戦略ガイド:ISR・CDN Cache・Runtime Cache・Cache Components(PPR) の4層を使い分ける

Vercel公式に忠実なキャッシュ実装ガイド。静的キャッシュ・ISR(stale-while-revalidate/31日永続/300msグローバルpurge/リクエスト併合)・CDN Cache(Cache-Control/CDN-Cache-Control/Vercel-CDN-Cache-Controlの3ヘッダ)・Runtime Cache・Cache Components(PPR)の4層を、on-demand revalidation(revalidatePath/revalidateTag/updateTag)とx-vercel-cacheの読み方まで実コードで解説します。

公開日
読了時間
10分
著者
友田 陽大
シェア

「ページが遅い」の解決策は1つではありません。Vercel は4つのキャッシュ層を提供しており、「レスポンス全体か/個別データ片か」「フレームワーク連携か/手動制御か」で使い分けます。層を取り違えると、キャッシュされていないのにされていると思い込む(そして Active CPU 課金とレイテンシで損をする)か、逆に古いデータを出し続ける事故になります。

この記事は ISRCDN CacheRuntime Cache の公式仕様に忠実に、4層を実コードで整理します。Next.js の Cache Components / PPR そのものの深掘りは Next.js 16 App Router の Cache Components 記事 に譲り、本稿は 「Vercel プラットフォームとしてどうキャッシュが効くか」 に集中します。全体像は Vercel 本番運用ガイド を参照してください。


4層の地図:まずどれを使うか

対象設定永続性代表的な用途
静的ファイルビルド出力(JS/CSS/画像/フォント)自動デプロイ寿命(ハッシュ名で跨ぎ持続)全デプロイ。考えなくてよい
ISRページ(HTML+データ)フレームワークAPI(revalidate等)31日・再検証までスケジュール更新のページ(EC・CMS・生成系)
CDN Cache関数のHTTPレスポンスCache-Control 系ヘッダ最大1年(ベストエフォート)ISR非対応・APIレスポンスを地域別にキャッシュ
Runtime Cache関数のデータ片Runtime Cache APIタグ無効化までfetch結果・DBクエリ・計算値の再利用

判断のショートカット:

  • ページ全体を出すフレームワーク(Next.js等)→ ISR(最強の機能群が自動で付く)
  • API のレスポンスを地域キャッシュ → CDN Cache(Cache-Control)
  • 関数の中で同じデータを何度も取るRuntime Cache
  • 静的アセット → 何もしない(自動

ISR:最も多機能なキャッシュ

stale-while-revalidate の挙動

ISR は「キャッシュ済みを即返し、裏で再生成」。Vercel の CDN に乗ることで、フレームワークの ISR は次を自動で得ます(ISR docs)。

  • 31日間の永続ストレージ:Function リージョンに永続。デプロイごとに独立したキャッシュ。
  • 300ms のグローバル purge:再検証時、全リージョンが 300ms 以内に更新。HTML とデータをアトミックに差し替える(全ページ遷移とクライアント遷移で内容が食い違わない)。
  • リクエスト併合:未キャッシュの同一パスに同時アクセスが殺到しても、リージョンごとに関数を1回だけ呼ぶ。スパイク時のオリジン保護。
  • 即時ロールバック耐性:過去デプロイのキャッシュは消えないので、ロールバックしても生成済みコンテンツを失わない。
  • キャッシュシールド:CDN ミス時、関数を呼ぶ前に永続 ISR キャッシュを読む。
// app/blog/[slug]/page.tsx
// 時間ベース再検証:3600秒ごとに裏で再生成
export const revalidate = 3600;

// 人気ページはビルド時に事前生成、それ以外はオンデマンド生成
export async function generateStaticParams() {
  const popular = await getPopularSlugs();
  return popular.map((slug) => ({ slug }));
}

export default async function Post({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  return <Article post={await getPost(slug)} />;
}

オンデマンド再検証:イベントで即時反映

「3600秒待たず、CMS で公開した瞬間に反映したい」——これがオンデマンド再検証です。revalidatePathrevalidateTag を使います。

// app/api/revalidate/route.ts — CMS の Webhook から叩く
import { revalidatePath, revalidateTag } from "next/cache";

export async function POST(request: Request) {
  // ① 認可(無防備な再検証エンドポイントは攻撃面になる)
  if (request.headers.get("authorization") !== `Bearer ${process.env.REVALIDATE_SECRET}`) {
    return new Response("Unauthorized", { status: 401 });
  }

  const { slug, tag } = await request.json();

  if (slug) revalidatePath(`/blog/${slug}`); // パス単位
  if (tag) revalidateTag(tag);               // タグ単位(複数ページ横断)

  return Response.json({ revalidated: true });
}

データ取得側でタグを付けておくと、revalidateTag("posts") のように横断的に無効化できます。

// fetch にタグを付ける(Next.js)
const posts = await fetch("https://cms.example.com/posts", {
  next: { tags: ["posts"], revalidate: 3600 },
}).then((r) => r.json());

再検証が失敗したら? Vercel はステイルを返し続け、30秒の TTL で再試行します。再検証成功とみなすステータスは 200/301/302/307/308/404/410 のみ。これ以外・ネットワークエラー・関数エラーは失敗扱いです。だから「再検証先が一時的に落ちても、ユーザーには古いページが出続ける」——可用性に効く設計です。

対応フレームワーク

ISR は Next.js 専用ではありません。

フレームワーク有効化
Next.js(App Router)ルートセグメントから revalidate を export
Next.js(Pages Router)getStaticPropsrevalidate を返す
SvelteKitconfigisr プロパティ
NuxtrouteRulesisr
Astroserver 出力で ISR 設定
GatsbyDSG(Deferred Static Generation)

Cache Components(PPR):静的シェルと動的穴の両立

Next.js 16 の Cache Components(Partial Prerendering, PPR) は、「1ページの中で、静的な殻(シェル)を即配信しつつ、動的な部分だけ穴を空けてストリームする」モデルです。use cache ディレクティブで境界を宣言し、cacheLifecacheTag で寿命とタグを制御します。

// app/dashboard/page.tsx
import { Suspense } from "react";

export default function Dashboard() {
  return (
    <>
      <StaticHeader />               {/* 静的シェル:即配信 */}
      <Suspense fallback={<Skel />}>
        <UserStats />               {/* 動的な穴:ストリーム */}
      </Suspense>
    </>
  );
}
// キャッシュ境界を宣言(コンポーネント/関数単位)
async function getCatalog() {
  "use cache";
  cacheLife("hours");     // 寿命プロファイル
  cacheTag("catalog");    // タグ付け → updateTag/revalidateTag で無効化
  return db.products.findMany();
}

Vercel 上では、この静的シェルが ISR/CDN に乗り、動的穴は Fluid Compute の関数がストリーミングで埋めます。タグ単位の無効化updateTag/revalidateTag)で、書き込み後に該当タグだけ即時更新できます。

Cache Components の API(use cachecacheLifecacheTagupdateTagunstable_cache からの移行)の詳細は、フロントエンドクラスタの Next.js 16 Cache Components 記事 に集約しています。本稿では「Vercel 上でどう配信されるか」だけ押さえれば十分です。


CDN Cache:関数レスポンスを3ヘッダで制御

ISR を使わない API・SSR でも、Cache-Control を返せばそのリージョンの CDN にキャッシュされます。Vercel はブラウザ/下流CDN/Vercel CDN を別々に制御できる3ヘッダを提供します(CDN Cache docs)。

// app/api/catalog/route.ts
export async function GET() {
  return Response.json(await getCatalog(), {
    headers: {
      "Cache-Control": "public, max-age=10",        // ブラウザ:10秒
      "CDN-Cache-Control": "public, s-maxage=60",   // 下流CDN:60秒
      "Vercel-CDN-Cache-Control": "public, s-maxage=3600", // Vercel:3600秒
    },
  });
}
ヘッダ制御先ブラウザに返るか
Cache-Controlブラウザ+(CDN指定がなければ)CDN
CDN-Cache-Control下流CDN・Vercel CDN(ブラウザは無視)
Vercel-CDN-Cache-ControlVercel CDN のみ❌(返らない・転送されない)

関数レスポンスを CDN にキャッシュするには s-maxage=N(任意で , stale-while-revalidate=Z)が必要です。CDN-Cache-Control を指定せず Cache-Control だけ書くと、Vercel はブラウザ送信前に s-maxage/stale-while-revalidate剥がします(ブラウザに内部キャッシュ方針を漏らさないため)。

キャッシュ可能条件(厳格)

レスポンスが CDN にキャッシュされるには、すべてを満たす必要があります。

  • リクエストが GET または HEADRange/Authorization ヘッダを含まない
  • レスポンスが 200/404/410/301/302/307/308
  • コンテンツ長 10MB 以下(ストリーミングは 20MB
  • Set-Cookie を含まない
  • Cache-Controlprivate/no-cache/no-store を含まない
  • Vary: * を含まない

AuthorizationSet-Cookie が鬼門:認証付き API がキャッシュされない最頻原因です。ログイン必須ページは基本キャッシュしない(または Vary とユーザー非依存部分の分離で設計)。

Vary でユーザー属性ごとに分ける

国・言語・デバイスで内容を変えつつキャッシュしたいときは Vary。Vercel は X-Vercel-IP-Country 等のリクエストヘッダをキャッシュキーに加えられます。

export async function GET(request: Request) {
  const country = request.headers.get("x-vercel-ip-country") ?? "unknown";
  return Response.json({ greeting: `Hello from ${country}` }, {
    headers: { "Cache-Control": "s-maxage=3600", Vary: "X-Vercel-IP-Country" },
  });
}

Vary は1つ増えるごとにキャッシュエントリが指数的に増える(=ヒット率が下がる)ので、本当に内容を変えるヘッダだけに絞ります。


Runtime Cache:関数内のデータ片をキャッシュ

レスポンス全体ではなく、関数ので「同じ fetch・同じ DB クエリ・同じ計算結果」を再利用したいとき。Runtime Cache はリージョンごとのエフェメラルな KV で、タグで無効化でき、Functions・Routing Middleware・Builds 間で共有されます。

// 高コストな計算を Runtime Cache に載せる(疑似コード)
import { getCache } from "@vercel/functions";

export async function GET() {
  const cache = getCache();
  const key = "exchange-rates";

  const cached = await cache.get(key);
  if (cached) return Response.json(cached); // データ片の再利用

  const rates = await fetchExchangeRatesFromUpstream(); // 高コスト
  await cache.set(key, rates, { tags: ["rates"], ttl: 300 });
  return Response.json(rates);
}

ISR や Cache-Control が「レスポンス全体」を扱うのに対し、Runtime Cache は「個別のデータ片」を扱うのが本質的な違いです。両者は併用できます(ページは ISR、ページ内の一部 fetch は Runtime Cache)。


デバッグ:x-vercel-cache を読む

「キャッシュされているつもりで、実は毎回関数が走っている」を防ぐ唯一の方法は、x-vercel-cache ヘッダを実際に見ることです。

curl -sI https://your-app.vercel.app/api/catalog | grep -i x-vercel-cache
# x-vercel-cache: HIT
意味
HITCDN キャッシュから配信(関数は走っていない)
MISSキャッシュになく、オリジン(関数)で生成
STALE期限切れだがステイルを配信、裏で再検証中
PRERENDERビルド時に事前生成された静的コンテンツ

本番リリース前に、キャッシュさせたいパスが HIT/PRERENDER になっているかを必ず確認してください。これが Active CPU 課金とレイテンシを直接左右します(コスト最適化ガイド)。


本番チェックリスト(キャッシュ)

  • キャッシュ層を4つから意図的に選択している(なんとなく SSR ではない)
  • ISR の revalidate を要件に合わせて設定、人気ページは generateStaticParams
  • オンデマンド再検証エンドポイントを認可している
  • 関数キャッシュは s-maxage を付け、3ヘッダの役割を理解
  • 認証付きレスポンスを誤ってキャッシュしていない(Authorization/Set-Cookie)
  • Vary は本当に内容を変えるヘッダだけ
  • x-vercel-cacheHIT/PRERENDER実測確認
  • CWV(LCP/INP/CLS)を Speed Insights で監視(CWV 最適化

まとめ

キャッシュは「速さ」だけでなく、コスト(Active CPU を呼ばない)と可用性(再検証失敗時もステイル配信) を同時に作ります。

  1. 層を選ぶ:ページは ISR、API は CDN Cache、データ片は Runtime Cache、静的は自動
  2. ISR の自動最適化(31日永続・300ms purge・リクエスト併合・ロールバック耐性)を活かす
  3. オンデマンド再検証で即時反映、Cache Components でシェルと穴を両立
  4. 3ヘッダで階層制御し、キャッシュ可能条件を満たす
  5. x-vercel-cache で必ず実測する

次は、作ったものを安全に出す デプロイ・CI/CD・ロールバック ガイド へ。

本記事は ISR / CDN Cache / Runtime Cache 公式ドキュメント(2026年6月時点)に基づきます。仕様は更新されるため、本番採用時は公式で最新値を確認してください。

友田

友田 陽大

経済産業大臣賞 受賞プロダクト開発者。TypeScript + Python + AWS で、SaaS・業界DX・ 実用レベルの生成AI(RAG)を、要件定義からインフラ・運用まで一人で完遂します。

この記事の実装を、案件として承ります

Vercel のアプリを、設計から本番運用・コスト最適化まで承ります

Fluid Compute を前提とした関数設計(グローバル状態の安全化・waitUntil・Cron)、4層キャッシュ(ISR/CDN/Runtime Cache/Cache Components)の設計、プレビュー/Promote/Instant Rollback/Rolling Releases の安全なデプロイ、Firewall/WAF/BotID の入口防御、Blob/Edge Config/Marketplace のストレージ選定、Active CPU 課金前提のコスト最適化まで。Next.js 製プロダクトを Vercel で本番運用してきた知見で、速く・安く・安全に伴走します。

プロジェクト単位(請負)・技術顧問のどちらにも対応可能です。まずは30分の無料技術相談から。

あわせて読みたい