Vercel CDNでFastAPIの静的ファイル配信を効率化する
Vercel CDNがFastAPIのapp.frontend()とStaticFilesを直接配信し、Function呼び出し、CPU、オリジン転送を減らす仕組みを解説します。料金への影響、ルート順序、ミドルウェア、依存関係の注意点を押さえ、公開ファイルと保護ファイルを分けて安全に検証する手順まで整理しました。

2026年9月10日、VercelはFastAPIアプリのコスト構造をひとつ変えました。対象となるフロントエンドと静的ファイルのリクエストは、Vercel CDNから訪問者へ直接配信されるようになり、Function Invocationsとコンピュート使用量を発生させなくなりました。ただし、CDN Requestsと転送量は引き続き発生します。つまり、静的配信が無料になるのではなく、Function利用が減る変更です。
Vercel CDNがFunctionを迂回する仕組み
静的ファイルとは、HTMLファイル、スタイルシート、JavaScriptバンドル、フォント、画像など、サーバーがそのまま返せるファイルです。Vercel Functionは、動的リクエストを処理する実行中のFastAPIプロセスを指します。CDNは、訪問者の近くでコンテンツを届ける配信レイヤーです。
この変更以前は、FastAPI経由でマウントしたファイルもFunctionの経路を通る場合がありました。リクエストはVercelのネットワークに届いた後、FastAPI Functionを呼び出し、Pythonがファイルを返していました。
現在はビルド時に、app.frontend()とFastAPIのStaticFiles配下から対象ファイルをVercelが検出します。該当ファイルはCDNへ昇格され、一致するリクエストはFunctionを呼び出さずに配信されます。
リクエストの経路は、次の状態から
visitor → CDN → FastAPI Function → file → visitor
次のように変わります。
visitor → CDN file → visitor
動的APIまで静的になるわけではありません。移動するのは、条件に一致するファイルだけです。プロジェクト直下のpublic/ディレクトリにあるファイルは以前からCDNで配信されていたため、公開アセットをすべてそこに置いているアプリでは、このリリースによる新たな削減効果はありません。
CDNへ昇格したディレクトリも、デフォルトではFunctionバンドル内に残ります。tool.vercel.fastapi.static.exclude = trueを設定すれば重複したコピーを削除できますが、これは別のバンドル設計上の判断です。CDN経由のリクエストを有効にするために、この設定は必要ありません。

減るのはコンピュート料金で、配信料金ではありません
Vercelは、ひとつのリクエストを複数のレイヤーで計測します。Function Invocationsはコードまで到達したリクエスト数です。Active CPUとProvisioned Memoryは、呼び出しの処理量と実行時リソースを表します。Fast Origin Transferは、CDNとFunctionの間で発生する通信量です。
一方、外側の配信メーターは別に計測されます。ダッシュボードでEdge Requestsと表示されるCDN Requestsは、静的アセットとFunctionトラフィックの両方が対象です。Fast Data Transferは、CDNから訪問者へ送ったバイト数を計測します。
デプロイ前はFunctionに到達し、デプロイ後は昇格の対象になる、条件が同じ100万件のアセットリクエストを整理すると次のようになります。
Proで公表されている料金は、Function呼び出し100万回あたり$0.60です。したがって、従来Functionまで届いていたファイルリクエスト100万件を移せば、月次クレジット適用前のFunction Invocations利用額が$0.60減ります。さらに、実際に消費していたActive CPU、Provisioned Memory、Fast Origin Transferも削減対象になります。
後半の削減量は、各プロジェクトのUsageデータで確認する必要があります。Pythonをほとんど動かさずに返していたファイルなら、減らせるCPUはわずかです。大きなレスポンスなら、削減できるオリジン転送量も増える可能性があります。このリリースが提供するのは新しい配信経路であり、一律の削減率ではありません。
CDN利用量は、別のコスト項目として残ります。Vercelの現行CDN料金ページでは、オンデマンド課金のProに最初の1,000万件のEdge Requestsと最初の1 TBのFast Data Transferが含まれ、その後はリージョン別の利用料金が適用されます。Flat Rate CDNを使うチームは、そのプランに設定されたリクエスト数と転送量の枠を消費します。この別枠の予算判断については、Flat Rate CDNの解説で詳しく扱っています。
Hobbyでは、現金支出が減る代わりに利用枠へ余裕が生まれる場合があります。HobbyにはFunction Invocations 100万回、Active CPU 4時間、Provisioned Memory 360 GB-hoursが含まれます。Functionに届かなくなった公開ファイルのトラフィックは、これらの枠を消費しません。ただし、CDN配信は該当するメーターに引き続き計上されます。
対象になるかはルートとセキュリティ設定で決まります
StaticFilesについて、VercelはFastAPIの宣言順序を維持します。静的マウントより前に宣言されたルートが同じパスにあれば、そのルートが優先されてFunctionへ到達します。マウントより後に宣言されたルートは、CDN上のファイルに優先されません。
app.frontend()の挙動は少し異なります。宣言順序にかかわらず、APIルートは常にフロントエンドファイルより優先されます。そのため、/にフロントエンドをマウントしても、実際のAPIエンドポイントが奪われることはありません。
セキュリティ設定によってもデフォルト動作は変わります。CDN上のファイルはPythonを通らないため、FastAPIのミドルウェアやDepends()によるチェックを実行できません。このためVercelは、次のパスをデフォルトでFunction側に残します。
- アプリにトップレベルのミドルウェアがある場合、そのアプリのすべての静的マウントとフロントエンド。
- サブアプリにミドルウェアがある場合、そのサブアプリの静的マウント。
- FastAPIの依存関係で保護されたフロントエンド。
cdn = falseは反対の選択です。昇格を無効にして、一致するすべてのリクエストをFunction側に残します。アプリケーションコードでリクエストごとの確認が必要なら、妥当な判断です。
小さなエッジケースもあります。StaticFilesをマウントしたルートそのものへのリクエストは、末尾にスラッシュを付けた形式へFastAPIがリダイレクトするため、引き続きFunctionに到達します。そのパスをクライアントが繰り返し要求する場合、Function利用量が完全なゼロになるとは考えないでください。
実務への影響
FastAPIリポジトリをひとりで運営するSaaS創業者
app.frontend()でバンドル済みWebアプリを配信している創業者なら、APIは動的なまま維持しつつ、公開HTML、CSS、JavaScriptのトラフィックをFunctionの経路から外せます。得られるのは、コンピュート利用の基準線が明確になることです。プロダクトのトラフィックが増えても、ブラウザによるアセット取得のたびにバックエンド処理として計上されなくなります。
公開ファイルと顧客限定ファイルを扱う制作会社のエンジニア
制作会社のエンジニアは、公開シェルをCDNに置きながら、機密性のあるルートを静的マウントより前に宣言できます。利点はFunction利用の削減だけではありません。どの顧客ファイルにPythonでのアクセス判定が必要で、どれには不要なのかをルート構成そのものが示すようになります。
財務部門と連携するバックエンド責任者
バックエンド責任者は、Function項目には動的なFastAPIトラフィック、CDN項目には訪問者向けの全トラフィックという形で、別々の予測を財務部門へ提示できます。これにより、Function Invocationsの減少をEdge Requestsや転送量も同じだけ減るものと誤解せずに済みます。
グローバルミドルウェアを使うチーム
トップレベルでロギング、テナント判定、認証のミドルウェアを使うチームでは、静的マウントがデフォルトでFunction側に残ると考えるべきです。実務上の検討事項は、明確に公開できるアセット領域だけがそのミドルウェアを迂回してよいかどうかです。認められないなら、この変更を根拠に安全な削減額を見込むことはできません。
ひとつのデプロイを監査する
拡張子があるファイルはすべて公開用だと決めつけず、実際のルート一覧から始めます。
公開パスと保護対象パスを分類する
すべての
app.frontend()ディレクトリとStaticFilesマウントを一覧にします。誰にでも返せるファイルを公開用として分類してください。ユーザー、テナント、ロール、Cookie、またはリクエスト時のミドルウェアの判定結果によってレスポンスが変わるものは、別に保護対象として分類します。宣言順序を確認する
StaticFilesマウントより優先すべきFunctionルートは、マウントより前に置きます。Vercelのドキュメントにあるパターンは次のとおりです。Pythonfrom fastapi import FastAPI from fastapi.staticfiles import StaticFiles app = FastAPI() # Declared before the mount, so this route wins over any CDN file at this path. @app.get("/static/protected.json") def protected(): return {"access": "denied"} app.mount("/static", StaticFiles(directory="static"))保護対象パスをFunction側に残す
設定を変える前に、トップレベルのミドルウェア、サブアプリのミドルウェア、フロントエンドの依存関係を確認します。グラフ上の数値を下げるためだけに
cdn = trueを設定してはいけません。影響するすべてのファイルが本当に公開用であることが条件です。一致する全トラフィックをFastAPIで処理する必要がある場合は、cdn = falseを使います。バンドルを縮小するか決める
実行中のアプリが昇格済みファイルを読み込まないなら、ドキュメントに記載された次の設定で、元のディレクトリをFunctionバンドルから除外できます。
TOML[tool.vercel.fastapi.static] exclude = true実行時のコードがそのファイルを引き続き必要とする場合は、デフォルト設定のままにします。
デプロイして経路分離を検証する
vc deployを実行します。公開ファイル、保護対象ファイル、APIルートをそれぞれひとつずつ呼び出してください。保護対象のレスポンスが正しく動作することを確認したうえで、プロジェクトのUsageにあるFunction Invocations、Active CPU、Provisioned Memory、Fast Origin Transfer、Edge Requests、Fast Data Transferを比較します。公開サンプルはCDNの集計から消えず、Functionの経路だけを外れているはずです。
削減効果を過大評価しない
ProのFunction呼び出し単価は100万回あたり$0.60にすぎません。小規模なアプリでは、現金支出の削減が数セントにとどまるか、月次利用クレジットの範囲内に収まる場合があります。より大きな利点は、Function利用量からアセットのノイズを取り除き、本来のAPI処理に使えるコンピュート枠を確保できることかもしれません。
公開アセットのトラフィックが多い場合、従来のFunction経路でメモリ保持時間が無視できないほど長かった場合、またはレスポンスが相応のFast Origin Transferを発生させていた場合は、削減効果が大きくなります。一方、ファイルがすでにpublic/にあった場合、トラフィックが少ない場合、セキュリティ規則によって正しくFunction側に残る場合は、効果も小さくなります。
この変更は、アセット最適化の代わりにはなりません。同じサイズの大きなJavaScriptバンドル、画像、フォントは、引き続きCDNから訪問者へ転送する必要があります。Fast Data Transferを減らすには、それらのファイル自体を小さくする取り組みが有効です。
月曜日に着手すべきこと
app.frontend()またはStaticFilesが公開ファイルを配信しており、そのリクエストがFunction利用量に現れているなら、今週対応する価値があります。ファイルが保護対象で、アクセス規則をまだ切り分けていないなら待つべきです。対象ファイルがすでにpublic/や別のCDN対応パスから配信されている場合、この変更の影響はありません。
月曜日には、公開アセットと保護対象アセットのパスを棚卸しし、実行が必要なルートとガードをすべて維持したうえで、各クラスからひとつずつリクエストしてデプロイを検証します。予算に反映するのは、実測したFunction利用量の減少です。CDN Requestsと転送量は、引き続き別の項目として残してください。
運用コストに影響するプラットフォーム変更を平易に読み解く記事を今後も受け取りたい方は、ニュースレターにご登録ください。
- 最終更新
- 2026年9月13日
- カテゴリー
- Explained







