Vercel Cache-Control設計テンプレ2026とNext.js実装

Vercelのrewriteを使っていると、「昨日までは速かったのに、今日から急に古いレスポンスが返る」という現象に出会いますよね。2026年4月以降はexternal-origin rewriteの既定挙動が変わったので、旧来の前提で作った構成が崩れやすくなっています。この記事は、Vercel Cache-ControlとNext.js 16.2 rewrite キャッシュを実務で安全に運用したい人向けです。読むと、鮮度・コスト・不整合の3つを同時に管理できる実装テンプレートを、そのまま今日から使える状態で持ち帰れます。僕は福祉事業のIT全般を担当しつつAI×SaaS開発も並行しているので、運用現場でハマったポイントまで含めて共有します。

こんな方におすすめ

  • Vercelのexternal-origin rewriteでキャッシュ挙動が読めず困っている
  • Next.js 16.2でrewriteを組み直す予定がある
  • 表示速度を上げたいが、更新反映の遅れは避けたい
  • 本番で壊さないCache-Control設計テンプレートがほしい

この記事でわかること

  • 2026年4月以降のVercel rewriteキャッシュ仕様の実務整理
  • ブラウザ・他CDN・Vercelの3層ヘッダ分離テンプレート
  • Next.js 16.2で安全にrewriteを適用する順番と実装コード
  • 事故時の復旧Runbookと本番チェックリスト

公開前確認(2026年5月時点):Vercelのexternal-origin rewriteは、新規プロジェクトでは2026年4月6日以降、上流の Cache-Control / CDN-Cache-Control / Vercel-CDN-Cache-Control を既定で尊重します。既存プロジェクトはダッシュボード設定や x-vercel-enable-rewrite-caching の有無で挙動が分かれるため、必ず本番URLで実測してください。

僕は福祉事業のIT全般を担当しながら、準委任でAI×SaaS開発にも入っています。複数の本番環境でrewriteとキャッシュの不整合を直してきた実体験ベースでまとめます。

2026年4月の変更点と、external-origin caching 2026で壊れる条件

最初に日付を固定します。Vercelは2026年3月30日のchangelogで、external-origin rewrite時に上流Cache-Controlを既定尊重する変更を発表し、新規プロジェクトでは2026年4月6日から適用されています。ここが大きな分岐点です。以前は「rewrite先は基本キャッシュされにくい前提」で設計していた人が多かったので、その設計のまま移行すると予期しないHITが増えます。

さらに混乱しやすいのが、Rewritesドキュメントに旧説明が一部残っている点です。つまり、プロジェクト作成時期とダッシュボード設定で挙動が分かれるので、ドキュメントだけで判断するとズレやすいです。まず「自分のプロジェクトはどのモードか」を確認することが最優先です

ケース 挙動 最初に確認する項目
2026-04-06以降に作成した新規プロジェクト 上流Cache-Controlを既定尊重 上流のTTLがそのまま効いていないか
既存プロジェクト(未opt-in) 旧挙動のままの可能性 ダッシュボード設定と実測ヘッダ
既存プロジェクト(opt-in済み) 新既定相当で動作 パス単位で例外が必要か
特定パスだけ除外したい ヘッダでopt-out可能 x-vercel-enable-rewrite-caching: 0
  • 確認1 – 本番でcurl -Iして、実際のTTLを見ます
  • 確認2 – 旧記事や社内Wikiの前提を2026年版に更新します
  • 確認3 – external rewriteは必ずステージングで再検証します

Vercel Cache-Controlを3層で分離する設計テンプレート

次に設計です。ここはVercel公式の優先順位が軸になります。優先順位はVercel-CDN-Cache-Control > CDN-Cache-Control > Cache-Controlです。つまり、同じレスポンスで3つ返せば、ブラウザ・他CDN・Vercelを分離できます。イメージとしては、同じ荷物に「倉庫向け」「配送向け」「店頭向け」の3枚ラベルを貼る感じです。

おすすめは、ブラウザ短め・Vercel長めで分離する構成です。たとえば、ブラウザは10秒、他CDNは60秒、Vercelは3600秒という分け方です。これなら体感速度を確保しつつ、頻繁更新のページだけは短いTTLにできます。さらにVercel Functionsのレスポンスヘッダはnext.config.jsvercel.jsonより優先されるので、最終判断はアプリ側で持つほうが事故が減ります。

Cache-Control: max-age=10
CDN-Cache-Control: max-age=60
Vercel-CDN-Cache-Control: max-age=3600

キャッシュされる条件も先に確認

  • メソッド – GET/HEADが前提です
  • ステータス – 200/404/410/301/302/307/308が対象です
  • 除外条件set-cookieAuthorizationがあると外れやすいです
  • サイズ上限 – non-streaming 10MB、streaming 20MBです

「TTLだけ決めて終わり」にしないことが重要です。キャッシュ条件を知らないままだと、想定どおりに載っていないのに気づけず、コストだけ上がる可能性があります。

Next.js 16.2 rewrite キャッシュを壊さない適用順

Next.js側は順番を間違えると意図せず全体がプロキシ化されます。公式の適用順は、headersredirectsbeforeFiles → 静的ファイル → afterFilesfallbackです。特に外部originへの退避をfallbackで使うときは、先にローカルの一致条件を絞っておくのが安全です。

そして2026年3月18日に出たNext.js 16.2は、開発サイクルがかなり速くなっています。next dev起動で最大約400%改善、描画は約50%改善、HTMLレンダリングも25〜60%改善の報告があります。つまり、rewriteの検証ループを回すコストが下がったので、設計を勘で決めるより小さく試すほうが得です。

  1. beforeFilesで明示的に許可 – APIなど明確な対象だけexternal rewriteします
  2. afterFilesで補完 – 静的アセットを食わないように後段に置きます
  3. fallbackは退避用 – 段階移行の受け皿として限定運用します
  4. trailingSlash整合 – 有効化時はsource/destination両方に末尾/を合わせます
// next.config.js
async rewrites() {
  return {
    beforeFiles: [
      { source: '/api/:path*', destination: 'https://api.example.com/:path*' }
    ],
    fallback: [
      { source: '/:path*', destination: 'https://legacy.example.com/:path*' }
    ]
  }
}

「何をrewriteしないか」を先に決めると、事故率が一気に下がります

実装テンプレート集: vercel.json・Route Handler・監視curl

ここからはそのまま貼って使える形です。まずはVercel側のrewrite定義です。新既定に合わせるなら、必要なパスだけ明示して、除外が必要なところだけx-vercel-enable-rewrite-caching: 0を当てる運用がわかりやすいです。

{
  "rewrites": [
    { "source": "/api/:path*", "destination": "https://api.example.com/:path*" }
  ],
  "headers": [
    {
      "source": "/api/live/:path*",
      "headers": [
        { "key": "x-vercel-enable-rewrite-caching", "value": "0" }
      ]
    }
  ]
}

次にRoute Handlerで3層ヘッダを返す例です。ここでTTLを役割分担しておくと、後の調整が最小変更で済みます。

// app/api/news/route.ts
export async function GET() {
  const data = await fetch("https://upstream.example.com/news").then(r => r.json());

  return Response.json(data, {
    headers: {
      "Cache-Control": "max-age=30",
      "CDN-Cache-Control": "max-age=120",
      "Vercel-CDN-Cache-Control": "max-age=1800"
    }
  });
}

最後に監視です。デプロイ後は最低でもこの3本を回します。ヘッダ監視をCIに入れるだけで、設定ドリフトにすぐ気づけます

curl -sI https://your-app.com/api/news | grep -Ei "cache-control|cdn-cache-control|vercel-cdn-cache-control"
curl -sI https://your-app.com/api/news | grep -Ei "x-vercel-cache|age"
curl -sI "https://your-app.com/page?_rsc=1" | grep -Ei "vary|cache-control"
  • 運用メモ – 仕様変更時に詰まったら、まず実ヘッダ・ダッシュボード設定・上流TTLを同じ表にまとめ、どの層でキャッシュされているかを切り分けます

よくある事故と復旧Runbook(SSE詰まり・古いキャッシュ・_rsc不整合)

僕が現場でよく見る事故は3つです。1つ目はSSEがrewrite経由でバッファされる問題です。2つ目はrevalidateTag/revalidatePathを叩いたのにCDNが古いまま残る問題です。3つ目は_rscを含むリクエストでVaryが足りず、ユーザーごとに表示がズレる問題です。Next.jsのCDNガイドでも、Data Cache更新だけではCDNは即時更新されない点が明記されています。

復旧手順の型

  1. SSE詰まり – rewriteを外してRoute Handlerの自前プロキシに切り替えます
  2. 古いキャッシュ残留 – 上流でタグを返し、タグ単位でinvalidateします
  3. _rsc不整合_rscをキャッシュキーに含め、Varyを不足なく返します
# 例: タグ無効化
vercel cache invalidate --tag post:123
// 例: Varyの明示
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Accept-Encoding
SSEは「たまに詰まる」状態がいちばん危ないです。再現率が低いので放置されやすいですが、障害対応の時間を一番溶かします。小さく再現環境を作って、rewriteとRoute Handlerの差を先に比較しておくと安心です。

復旧を速くしたいなら、手順を文章ではなくコマンドで残すのがおすすめです。夜間対応で判断がブレにくくなります。

本番運用チェックリスト: タグ無効化・段階ロールアウト・コスト監視

実装が終わったあとに差が出るのは運用です。ここは「デプロイできたか」ではなく「期待どおりに更新されるか」で判断します。特にrewrite周りは、機能テストが通ってもキャッシュ戦略がずれていると、後からコストと鮮度が同時に悪化しやすいです。本番は段階ロールアウト前提で進めるのが安全です

  1. 新規/既存の挙動差を記録 – 2026-04-06境界の前提を運用ドキュメント化します
  2. TTLの責務分離を固定 – 3層ヘッダをテンプレート化して再利用します
  3. _rscとVaryを検証 – App Router経由のレスポンスを専用にチェックします
  4. タグ運用を導入 – 対象ページ単位でinvalidateできる粒度にします
  5. 段階リリース – まず一部パスのみ新TTLへ切り替えます
  6. 指標監視 – HIT率、TTFB、origin転送量、エラー率を同時に見ます
  7. サイズ監視 – 10MB/20MB上限を超えるレスポンスを検知します
  8. 週次レビュー – 「速いが古い」「新しいが遅い」の偏りを修正します
  • 補足 – 設計レビューでは、対象パス・上流ヘッダ・除外条件・タグ無効化手順を1枚にまとめてから確認すると、見落としが減ります

この変更を知らないまま1年運用すると起こること

この仕様差を知らないままだと、まず「更新したのに反映されない」問い合わせが増えます。次に、originへの無駄なアクセスが増えてコストが読めなくなります。最後に、_rscやVaryの不整合でユーザーごとに違う画面が見える可能性があります。どれも一気に壊れるというより、じわじわ信用を削るタイプです。気づいた時には原因が分散していて直しづらいので、今のうちに設計をそろえておくのが安心です。

この記事を書いている理由

僕自身、2025年に運用したWeb招待状サービスで90日以上のログイン継続率を追いながら、表示速度と更新反映のバランスにかなり苦労しました。2026年4月2日にはVercelの既定変更チェックリストも書いたんですが、実際に案件へ入ると「知識だけ」では守り切れない場面が多かったです。だから今回は、Next.js 16.2前提で壊さない実装手順まで落としてまとめました。僕がハマったところを先回りで共有できれば、同じ遠回りを減らせると思ったからです。

次のアクション

まずは本番URLに対してcurl -Iを3本だけ実行して、現状のヘッダとVaryを確認してみてください。ここが見えるだけで、次の修正優先度が一気に明確になります。

今日からできるアクション

  • 30分で現状把握 – rewrite対象パスの実ヘッダを採取して一覧化する
  • 1時間でテンプレ適用 – 3層Cache-Controlとfallback順序を固定する
  • 検証ログを残す – 実測ヘッダ、HIT率、origin転送量、除外パスを週次で見直す