Vercel4/6変更対応 external-origin rewrite監査術

「external-origin rewriteは未キャッシュ前提」で運用してきたけど、4月以降どう直せばいいのか迷っている人に向けて書きました。この記事では、2026年4月6日からのVercel CDN既定変更に合わせて、Cache-Control監査の進め方、事故を防ぐ実装、ロールバックまでを1本で整理します。僕は福祉事業のIT全般を担当しながらAI×SaaS開発にも入っているので、現場でそのまま使える粒度でまとめます。

こんな方におすすめ

  • Vercelのexternal-origin rewriteを本番運用している
  • Cache-Controlをなんとなく設定していて不安がある
  • 4月6日以降の既定変更でstale配信が怖い
  • 最小の工数で監査チェックリストを作りたい

この記事でわかること

  • Vercel 2026-04-06 変更で何が実務的に変わるか
  • external-origin rewriteで起こる事故パターンと回避策
  • vercel.jsonとorigin headerの実装テンプレート
  • 今日から使える検証Runbookと無効化オペレーション

公開前確認(2026年5月時点):Vercelのexternal-origin rewriteは2026年4月6日以降、新規プロジェクトで上流Cache-Controlを既定で尊重します。既存プロジェクトでは設定差が出るため、curlで実ヘッダを確認してください。

僕は福祉事業のIT全般を担当しつつ、複業でAI×SaaS開発をしています。仕様変更前に監査項目をチェックリスト化して運用事故を減らすやり方を続けてきたので、今回も「実装者が迷わない順番」で解説します。

2026-04-06のVercel変更点:external-origin rewriteとCache-Controlの前提が逆になる

2026年3月30日の公式Changelogで、2026年4月6日から新規Vercelプロジェクトではexternal-origin rewriteのレスポンスがupstreamのCache-Control系ヘッダーを既定で尊重してCDNキャッシュされると明示されました。これまでの「外部rewriteは未キャッシュ寄り」という運用感覚から、前提が逆になります。つまり、originのTTL設計がそのまま配信品質とコストを決める状態になります。

比較項目 2026-04-05までの既定 2026-04-06以降(新規PJ) 既存PJ
external-origin rewrite 未キャッシュ前提で扱われやすい upstreamのCache-Controlを既定で尊重 Dashboardから段階的にopt-in
個別パス制御 x-vercel-enable-rewrite-caching: 1 が中心 x-vercel-enable-rewrite-caching: 0 でopt-out 同じくヘッダーで制御可能
運用の主戦場 Vercel側設定中心 origin header設計が最重要 移行時の差分監査が最重要

ここでややこしいのが、Rewritesドキュメント(最終更新2026年3月5日)にはまだ「external originsは既定で未キャッシュ」という表現が残っている点です。ドキュメントとChangelogの時差があるので、「見たページが最新前提」という思い込みは危険です。Vercel CDNは126以上のPoPsで配信されるので、1つのヘッダー設計ミスが一気に広がる可能性があります。

external-origin rewriteで起きるキャッシュ事故パターン3つ

ここからは、僕が実務でよく見る事故を3つに絞って整理します。Vercel CDN Cache-Controlは便利ですが、成立条件(GET/HEAD、Authorizationなし、set-cookieなし、サイズ制約など)を満たした瞬間にキャッシュが走るので、前提の置き方を間違えると一気に表面化します。イメージとしては、便利な自動ドアに「立入禁止」の貼り紙を忘れる感じです。動作自体は正しいのに、運用意図とズレます。

  • 機密レスポンスの残留 — 認可不要に見えるURLへprivate情報を返すと、想定以上に長く残る可能性があります。
  • 二重CDNの無効化ズレ — Cloudflareなどを前段に置く構成だと、どちらかだけ更新されてstale配信が続きやすいです。
  • コストの読みにくさ — max-age/s-maxageの設計が曖昧だと、ヒット率と再検証頻度がぶれて運用費が安定しません。

事故が増えやすい条件

  • 「ブラウザ向けTTL」と「CDN向けTTL」を同じ値で運用している
  • Varyヘッダーを増やしすぎてキャッシュキーが分散している
  • 全体purgeだけで更新運用していて、粒度の細かい無効化がない

まずは事故パターンを先に共有し、チームの前提を揃えるのがおすすめです。これだけで実装の迷いがかなり減ります。

本番前にやる監査チェックリスト:originヘッダー・TTL・Vary・機密ルート

ここはそのまま会議に貼れるように、順番付きで置いておきます。ポイントは、Vercelのキャッシュ優先順位が Vercel-CDN-Cache-Control > CDN-Cache-Control > Cache-Control という点です。つまり同じレスポンスでも、ブラウザに見える値とVercel内部TTLがズレることがあります。ズレを設計するのはOKですが、ズレを知らずに運用するのはNGです。

  1. 対象URL棚卸し — external-origin rewriteのパスを全件出し、public/会員限定/管理系に分類します。
  2. origin実ヘッダー採取 — 本番相当環境でcurl -Iし、現行のCache-Control系列を収集します。
  3. TTL方針決定 — ブラウザ向けmax-ageとCDN向けs-maxageを分離し、更新頻度に合わせます。
  4. Vary最小化 — 不要なVaryを外し、キャッシュキー分散を抑えます。
  5. 機密系opt-outx-vercel-enable-rewrite-caching: 0 をprivate系に適用します。
  6. 無効化設計 — 全purgeではなくtag invalidateを標準手順にします。
  7. 監視項目定義x-vercel-cache、Age、4xx/5xxの閾値を決めます。
運用で迷いやすいのは「どの環境で採取したヘッダーを真実とするか」です。僕は必ず本番同等ルートで採取して、ステージングとの差分を別表にしています。ここを曖昧にすると、検証だけ通って本番で崩れます。

実装手順:vercel.json・origin header・Cache Tag無効化

ここからはコピペ起点で進められるように、最小構成を載せます。2026-04-06以降は新規プロジェクトで既定キャッシュが有効なので、まず「キャッシュしてはいけない場所」を明示する設計が安全です。

1. vercel.jsonでrewriteとopt-outパスを定義

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

運用メモとして、外部導線ではなく、実測ログ・設定差分・再現手順を同じ場所に残してチーム内で確認できる形にしてください。

2. originで3層TTLを分離

Cache-Control: max-age=10
CDN-Cache-Control: max-age=60
Vercel-CDN-Cache-Control: max-age=3600
Vercel-Cache-Tag: product-123,category-electronics

更新頻度が高い画面はブラウザTTLを短く、CDNは少し長く、Vercel内部はさらに長く設定すると安定します。さらにタグを付けておけば、対象だけ無効化できます。

3. 必要な単位だけinvalidate

vercel cache invalidate --tag product-123
# 緊急時のみ
vercel cache purge --type cdn

全体purgeは最終手段です。普段はタグ無効化で影響範囲を小さく保つのがおすすめです。

検証Runbook:curl・x-vercel-cache・Observabilityで15分確認

実装が終わったら、次は「想定どおりに振る舞っているか」を15分で確認します。ここを省くと、変更した気になって終わるので注意です。僕は毎回同じRunbookを回して、チームの検証品質を揃えています。

  1. 1回目リクエストcurl -I https://your-domain.com/api/products/123 で初回MISSを確認します。
  2. 2回目リクエスト — 同コマンドを再実行し、x-vercel-cache: HIT へ遷移するか見ます。
  3. ヘッダー整合性確認 — Age、Cache-Control系列、Varyを記録し、設計値と突き合わせます。
  4. タグ無効化試験 — invalidate後にMISSへ戻るか確認し、影響URLを限定できるか見ます。
  5. 観測画面確認 — External Originsのメトリクスでヒット率と再検証頻度をチェックします。
curl -I https://your-domain.com/api/products/123
curl -I https://your-domain.com/api/products/123
vercel cache invalidate --tag product-123
curl -I https://your-domain.com/api/products/123

判定の目安

  • 期待値: MISS → HIT → invalidate後MISS
  • 要確認: 常にMISS(成立条件未達の可能性)
  • 要対応: 想定外HIT(機密ルートのopt-out漏れの可能性)

運用メモとして、外部導線ではなく、実測ログ・設定差分・再現手順を同じ場所に残してチーム内で確認できる形にしてください。

新規/既存プロジェクト別の移行計画とロールバック方針

最後に、現場で一番迷う「どの順番で移行するか」を整理します。新規プロジェクトは4月6日以降すぐ影響を受けるので、初日から監査前提で進めるのが安全です。既存プロジェクトはDashboard opt-inのタイミングをコントロールできるので、段階移行が向いています。一気に全部切り替えるより、URL群を小分けで進めるほうが再現性が高いです。

区分 初動 1週間以内 ロールバック
新規PJ(4/6以降) private系をopt-outで開始 public系をTTL最適化 対象パスを0に戻す
既存PJ 監査完了ルートのみopt-in tag運用と監視を定着 opt-in範囲を段階縮小
二重CDN構成 無効化順序を文書化 両CDNのTTL差を調整 前段CDNを一時bypass
  • フェーズ1 — URL棚卸しと機密ルート遮断を先に完了させます。
  • フェーズ2 — publicルートを小さく開放し、HIT率と障害率を観測します。
  • フェーズ3 — タグ無効化を標準化して、全体purge依存を減らします。

比較軸を増やしたい人は、entry-12891470751の比較表entry-12877474895の運用記事を並べて読むと、判断基準が揃いやすいです。

Cache-Control監査を後回しにすると起きる現実

この変更を知らないまま運用すると、静かに品質が落ちるのがいちばん怖いです。たとえば、機密寄りAPIが想定より長く残る、二重CDNで無効化順序がズレる、全体purge連発で障害対応コストが増える、という流れが起きます。「動いているからOK」ではなく「意図どおりにキャッシュされているか」を見るだけで、1年後の運用品質はかなり変わります。

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

僕自身、2025年に結婚式Web招待状サービスを出したとき、90日以上ログイン継続率を守るために「速度」と「更新反映」を両立させる難しさを痛感しました。さらに、今は福祉事業のIT全般を担当していて、仕様変更の前に監査項目をチェックリスト化する運用を日常的に回しています。週1ペースのLTで、変更点を実装手順へ翻訳して共有すると現場で使われやすいと実感しているので、今回もその形でまとめました。

次のアクション

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

  • external-origin rewriteのURL棚卸しを30分で実施する
  • private系に x-vercel-enable-rewrite-caching: 0 を先に入れる
  • タグ無効化を試し、全体purgeを緊急時だけに限定する
  • 運用メモとして、外部導線ではなく、実測ログ・設定差分・再現手順を同じ場所に残してチーム内で確認できる形にしてください。

実装してみて詰まった点があれば、コメントかSNSで気軽に聞いてください。必要なら今回のRunbookをあなたの構成向けに調整して、一緒に事故を減らしていきます。