所感 オーケストレーションを組んでからClaude(MAX)の使用量を全然埋められないのでめっちゃAIに任せてやりました。とはいえ最近でたフレームワーク、AWS機能を試せて比較できたのはAIあってこそだったかなと。
Lambda Web Functions では速さはほぼ互角、差はコストに出た:SvelteKit 3 / TanStack Start / Next.js 16.4 比較
TL;DR
- 1 リクエストの速さはほぼ互角だった。商品詳細ページの完了 p50 は 340〜349ms で、ほとんどがモック API の待ち時間。
- 差が出たのは CPU。1 リクエストあたり SvelteKit 5.7ms、TanStack 7.7ms、Next.js 系 18.7〜27.5ms。Lambda ではこれがそのままコストの差になり、Next.js は約 2 倍。
- コールドスタートは Next.js 2.6 秒、TanStack 0.93 秒(各 1 回の計測)。
- 計測全体の費用は約 $0.96。その約 9 割は、負荷で増えた実行環境のメモリ課金だった。
- 3 つとも、アダプターなしで数行の入口ファイルを足すだけで Lambda Web Functions で動いた。
- AWS の中で動くので、CloudFront と WAF を前段に置いて、IP 制限や攻撃対策をアプリの外で持てる。
目次
- コード
- はじめに
- 3 つのフレームワークの注目ポイント
- 検証の進め方
- 検証用に作ったアプリ
- この記事で使う言葉
- 計測を読む前に:RPS の上限
- 第 1・2 回:Node コンテナでの計測
- 第 3 回:AWS Lambda Web Functions での計測
- Lambda Web Functions を選ぶメリット
- まとめ
- 注意・まだ見ていないこと
コード
検証に使ったアプリ・計測スクリプト・CDK はすべて公開しています。
- 検証アプリと計測スクリプト:https://github.com/reeschan/reeschan-fw-bench-ec
- Lambda Web Functions 用の CDK L2:https://github.com/reeschan/cdk-lambda-web
はじめに
2026 年 10 月、サーバー機能を内蔵したフルスタックフレームワークのリリースが立て続けにありました。
- SvelteKit 3.0(10/1 GA):Svelte 5 + Vite 8
- Next.js 16.4(10/6):React 19.3、Cache Components
- TanStack Start 1.0(10/13 stable):React + Vite + Nitro
さらに 10/12 には、Lambda の新機能 AWS Lambda Web Functions が発表されました。普通の Node.js の HTTP サーバーを、アダプターなしでそのまま Lambda に載せられます。
3 つのフレームワークは、どれも素の Node サーバーとして動かせます。それなら「同じアプリを 3 つで作って、Node コンテナと Lambda Web Functions の両方で比べたらどうなるか」をやってみよう、というのが今回の動機です。
見たかったのは次の点です。
- 1 リクエストの速さ(TTFB と、レスポンスが最後まで届く時間)
- 負荷をかけたときのスループットと p99
- コールドスタート
- Lambda Web Functions で動かしたときの、1 リクエストあたりのコスト
3 つのフレームワークの注目ポイント
SvelteKit 3
- Svelte 5 と Vite 8(Rolldown)が前提になりました。Svelte はコンパイラなので仮想 DOM のランタイムを持たず、クライアントに送る JS が小さくなりやすいのが特徴です。
- 設定が
svelte.config.jsからvite.config.tsに移りました。 アダプターもsveltekit({ adapter })のように、Vite プラグインのオプションで指定します(SvelteKit 3 の発表)。 $libは#lib(package.json のimports)に変わりました。- サーバー側のデータ取得は、従来どおり
load関数と form actions です。新しい Remote functions は、3.0 でもまだ experimental です。
TanStack Start 1.0
- TanStack Router をベースにしたフルスタックフレームワークで、React と Solid に対応しています(TanStack Start 1.0 の発表)。
createServerFnを使うと、サーバーだけで動く関数を書いて、クライアントから普通の関数のように呼べます。ほかに full-stack middleware やcreateIsomorphicFnもあります。- Selective SSR:ルートごとに「SSR する」「データだけサーバーで取る」「クライアントで描画する」を選べます。
- ビルドとデプロイは Vite + Nitro です。独自のアダプターはやめて、Nitro の preset でデプロイ先を切り替えます。
- React Server Components(RSC)には依存せず、クラシックな SSR + ハイドレーションが基本です。
Next.js 16.4 と Cache Components(Next CC)
- React 19.3 を同梱し、ビルドは Turbopack が標準です。
- 今回の比較で一番の注目は Cache Components です。新規アプリではデフォルトで有効で、Next 17 では全体のデフォルトになる予定とされています(Next.js 16.4 の発表、cacheComponents の設定)。
Cache Components とは
ページを「事前に作っておける静的な部分(シェル)」と「リクエストごとに作る動的な部分」に分ける仕組みです。静的シェルを即座に返し、動的な部分は後からストリーミングで流します。
- データを待つ部分は
<Suspense>で囲みます。囲んでいない部分が、静的シェルとしてビルド時に作られます。 - キャッシュしてよいデータには
'use cache'を付け、cacheLife('minutes')などで有効期間を決めます。付けなければ毎回取得します。 'use cache'のキャッシュは、既定では各サーバーのメモリに置かれます。Lambda では実行環境ごとに別々なので、新しく立ち上がった環境では最初はキャッシュがありません。
この記事では、Cache Components の OFF / ON / ON + 'use cache' の 3 パターンで比べます(詳しくは後述の「Next.js は 3 パターン」)。
検証の進め方

アプリの実装と Node コンテナでの計測は、クラウド上の作業環境で行いました。Lambda Web Functions へのデプロイは、AWS SSO のプロファイルがある手元の Windows PC からです。計測が終わったら、AWS 上のリソースは全部削除しています。
検証用に作ったアプリ
どんなアプリか
架空の EC サイト「Bench Store」です。商品一覧、商品詳細、JSON API の 3 ルートだけの小さなアプリですが、EC サイトで性能に効く要素をひととおり入れました。
- SEO が必要な SSR ページ
- DB や外部 API を待つ I/O
- 遅い API を後からストリーミングで流す部分
- クライアント側で動く部品(カートボタン)
これを SvelteKit / TanStack Start / Next.js の 3 つで、同じ仕様・同じ CSS・同じ見た目で実装しています。

商品一覧(/):カテゴリナビと商品カード 24 件、ページャがあります。

商品詳細(/products/:id):商品情報と在庫、カートに入れるボタン、おすすめ商品があります。

データはモック API から
DB の代わりに、商品 1,000 件を返すモック API(data-api)を用意しました。データは毎回同じものを生成し、応答に人工的な遅延を入れて、DB や外部 API を待つ状況を再現しています。
| API | 遅延 |
|---|---|
| 商品一覧 | 50ms |
| 商品 1 件 | 50ms |
| 在庫 | 30ms |
| おすすめ | 300ms(わざと遅くしている) |
3 つで揃えたこと
商品詳細ページの作りは 3 つとも同じです。

- 一番遅いおすすめ(300ms)の取得を最初に始める
- 商品と在庫を並列で取得する
- 商品・在庫・カートの「殻」を先に返し、おすすめ部分は同じレスポンスの中で後から流す(ストリーミング)
ほかにも次の点を揃えました。
- データ取得はすべてサーバー側で行う(TanStack は
createServerFn経由) - データのキャッシュはしない(毎回 data-api に取りに行く)
- 本番ビルドを素の Node サーバーとして動かす
- SvelteKit は adapter-node
- TanStack Start は Nitro の node-server
- Next.js は
output: 'standalone'
Next.js は 3 パターン
Next.js 16 の目玉の Cache Components も見たかったので、Next.js だけ 3 パターン用意しました。
| 名前 | 内容 |
|---|---|
| Next | Cache Components OFF(基準) |
| Next CC | Cache Components ON。レイアウトを静的シェルとして事前生成し、ページ本体は Suspense の中に入れる。データキャッシュはなし |
| Next cached | Next CC に 'use cache' を追加し、一覧・商品・おすすめをキャッシュ(在庫だけ毎回取得)。他にはキャッシュがないので参考値 |
実装でハマったところ
- TanStack Start は、ボットっぽい User-Agent だとストリーミングしません。 curl や負荷ツールのデフォルト UA だと、全部描画し終わるまで待ってから返します。計測はすべてブラウザの UA で行いました。
- TanStack Start で検索パラメータにデフォルト値を入れると、
/が 307 リダイレクトになりました。任意パラメータにして回避しています。 - SvelteKit 3 では
$libの代わりに#lib(package.json の imports)を使うようになり、tsconfig は$app/tsconfigを継承する形に変わっていました。
この記事で使う言葉
| 言葉 | 意味 |
|---|---|
| TTFB | Time To First Byte。リクエストを送ってから、最初の 1 バイトが返ってくるまでの時間。画面が描き始められるまでの早さの目安 |
| 完了時間 | レスポンスが最後まで届くまでの時間。ストリーミングしていると、TTFB より後になる |
| p50 / p95 / p99 | 全リクエストを速い順に並べたとき、50% / 95% / 99% の位置にある値。p50 は「真ん中(中央値)」、p99 は「100 回に 1 回の遅いケース」。平均だと隠れてしまう、たまに遅いリクエストの影響を見るのが p99 |
| RPS | Requests Per Second。1 秒間にさばけたリクエスト数。大きいほどたくさんの人を同時に相手にできる |
| 50 同時 | 負荷ツールが 50 本の接続を開き、それぞれが応答を待ってから次のリクエストを送る負荷のかけ方 |
| コールドスタート | サーバー(コンテナや Lambda の実行環境)が起動していない状態から、最初のリクエストに応答するまでの時間 |
| SSR | Server Side Rendering。HTML をサーバーで組み立ててから返すこと |
| ストリーミング | HTML を全部できあがるまで待たず、できた部分から順に送ること |
| 静的シェル | ページのうち、リクエストごとに変わらない外枠(ヘッダーやレイアウト)。事前に作っておける |
| クライアント JS | ブラウザがダウンロードして実行する JavaScript。gzip は圧縮後のサイズ |
| 実行環境 | Lambda がアプリを動かす小さなサーバー 1 台分。負荷に合わせて自動で増えたり消えたりする |
| vCPU 秒 / GB 秒 | Lambda の課金単位。CPU を 1 個 1 秒使うと 1 vCPU 秒、メモリ 1GB を 1 秒確保すると 1 GB 秒 |
計測を読む前に:RPS の上限
負荷テストには autocannon を使い、50 本の接続でリクエストを送りました。autocannon は、それぞれの接続で応答を待ってから次のリクエストを送ります。そのため RPS には、計測方法で決まる上限があります。
RPS の上限 ≈ 接続数 50 ÷ 1 回の応答時間
商品詳細ページは、おすすめ API を待つので完了まで約 300〜340ms かかります。そのため上限は、Node コンテナで約 160(50 ÷ 0.31 秒)、Lambda で約 150(50 ÷ 0.34 秒)です。この数字に張り付いているときは、フレームワークの差ではなく「計測方法の上限」を見ています。CPU の差は、上限より下に落ちているかどうか、あるいは CPU 時間そのもので見るのが確実です。
第 1・2 回:Node コンテナでの計測
計測環境

各アプリは Docker で CPU 1 コア / メモリ 512MB に制限し、1 つずつ起動して計測しました。
| 計測項目 | 方法 |
|---|---|
| TTFB / 完了時間 | 直列に 200 回リクエストし p50 / p95(ウォームアップ 20 回) |
| スループット | autocannon で 50 接続 × 15 秒。RPS と p99 |
| メモリ | 負荷中の docker stats |
| コールドスタート | コンテナを起動してから最初に 200 が返るまで |
| クライアント JS | ページが読み込む JS の合計(gzip) |
負荷ツールとアプリが同じマシンで動いているので、絶対値よりも相対的な差を見てください。
第 1 回の結果(3 フレームワーク)
太字は各行の最良値です。
| SvelteKit | TanStack | Next | |
|---|---|---|---|
| 詳細 TTFB / 完了 p50 | 57 / 306ms | 58 / 307ms | 66 / 315ms |
| 一覧 RPS(50 同時) | 255 | 182 | 50 |
| 詳細 RPS(50 同時) | 154 | 109 | 41 |
| API RPS(50 同時) | 454 | 410 | 135 |
| 詳細 p99(50 同時) | 483ms | 723ms | 4,544ms |
| クライアント JS(gzip) | 34KB | 108KB | 174KB |
| 負荷時ピークメモリ | 54MB | 63MB | 223MB |
| コンテナ起動 → 最初の 200 | 901ms | 922ms | 1,555ms |
- 1 リクエストの速さはほぼ互角でした。I/O 待ち(50ms)がほとんどを占めていて、差は 10ms 程度です。
- 差が開いたのは CPU 1 コアで負荷をかけたときです。Next の RPS は、一覧・詳細・API のどれでも SvelteKit の 1/3〜1/5 でした。p99 は秒単位まで悪化しています。
- 詳細ページでは、SvelteKit の 154 RPS は上限(50 ÷ 0.306 ≈ 163)にほぼ届いています。Next の 41 は上限よりはるかに下で、CPU が詰まっていると考えられます。
- クライアント JS は Svelte のコンパイル出力が最小でした。
- コールドスタートは Next が SvelteKit の約 1.7 倍かかりました。
第 2 回の結果(Next.js の Cache Components を追加)
※ 第 1 回とはマシンの状態が違うので、数値は同じ表の中だけで比べてください。Next cached はキャッシュありの参考値です。
| SvelteKit | TanStack | Next | Next CC | Next cached(参考) | |
|---|---|---|---|---|---|
| 一覧 TTFB p50 | 56ms | 57ms | 63ms | 4.5ms | 4.6ms |
| 詳細 完了 p50 | 305ms | 306ms | 311ms | 319ms | 50ms |
| 一覧 RPS(50 同時) | 427 | 274 | 83 | 43 | 47 |
| 詳細 RPS(50 同時) | 162 | 156 | 67 | 38 | 38 |
| 詳細 p99(50 同時) | 367ms | 446ms | 1,283ms | 2,795ms | 4,713ms |
- Cache Components を ON にすると、TTFB は 60ms → 5ms になりました。静的シェルを即座に返すためです。
- ただし中身が最後まで届く時間は変わりません。データ待ちはそのまま残るからです。
- 負荷時は、ON にするとむしろ悪くなりました。 一覧の RPS は 83 → 43、詳細の p99 は 1,283ms → 2,795ms です。
- 詳細 RPS は、SvelteKit(162)と TanStack(156)が上限(50 ÷ 0.305 ≈ 164)近くに張り付いています。Next の 3 パターンは上限よりはるかに下です。
'use cache'を足すと、単発の完了時間は大きく縮みます(311ms → 50ms)。それでも Next CC と比べた RPS はほぼ同じ(一覧 43 → 47、詳細 38 → 38)で、詳細の p99 は 4,713ms と 5 つの中で一番悪くなりました。CPU 1 コアでは、データ待ちではなくレンダリングの CPU がボトルネックで、キャッシュで待ち時間が減っても処理待ちの行列が伸びただけだと考えています(推測)。
第 3 回:AWS Lambda Web Functions での計測
Lambda Web Functions とは
- ポートで待ち受ける普通の HTTP サーバーを、そのまま Lambda で動かせる
- 1 つの実行環境で複数のリクエストを同時に処理する(既定は最大 64)
- 課金は「実際に CPU を使った時間」「環境が生きている間のメモリ」「リクエスト数」。DB を待っている間の CPU には課金されない
- 提供開始時点のランタイムは Node.js 24(arm64)のみ。2 vCPU / 2GB 固定
3 つのフレームワークの Node サーバー出力を、ほぼそのまま載せられます。違いは待ち受けアドレスだけです。このアドレスは環境変数 AWS_LAMBDA_HTTP_ENDPOINT(host:port)で渡されるので、各フレームワークが読む HOST / PORT に詰め替える数行の入口ファイルを足しました。
// lwf-entry.mjs
const ep = process.env.AWS_LAMBDA_HTTP_ENDPOINT ?? '0.0.0.0:3000';
const i = ep.lastIndexOf(':'); // IPv6 のホストでも壊れないように最後の : で分ける
process.env.HOST = process.env.HOSTNAME = ep.slice(0, i);
process.env.PORT = ep.slice(i + 1);
await import('./build/index.js'); // フレームワークごとのサーバー
従来の Lambda と何が違うのか
Lambda でも、これまで Web ページを返すことはできました。Function URL や API Gateway の後ろに Lambda を置き、Lambda Web Adapter や各フレームワークのアダプター(OpenNext など)でリクエストを変換する方法です。Web Functions の登場で、従来の Lambda は「Lambda Event Functions」と呼ばれるようになりました(AWS の発表)。

| 従来の Lambda(Event Functions) | Lambda Web Functions | |
|---|---|---|
| 受け取るもの | イベント(JSON)。HTTP に戻すアダプターが必要 | HTTP そのもの。普通の Node サーバーが動く |
| 同時処理 | 1 つの環境で 1 リクエスト | 1 つの環境で複数(既定は最大 64) |
| 課金 | 実行時間 × 設定メモリ(I/O 待ちの時間も含む) | CPU を使った時間 + 環境が生きている間のメモリ + リクエスト数 |
| ストリーミング | 専用のハンドラー(streamifyResponse)で書き、Function URL など対応した経路から呼ぶ必要がある | 普通の HTTP サーバーのまま、そのままストリーミングできる |
いちばん大きいのは課金の考え方です。従来の Lambda では、DB や外部 API を待っている時間もまるごと課金されました。Web Functions では待ち時間の CPU は課金されず、しかも 1 つの環境が待っている間に別のリクエストを処理できます。今回のアプリのように、処理時間の大半が I/O 待ちのページほど得をします。
CDK の L2 を自作した
デプロイは AWS CDK で書きたかったのですが、aws-cdk-lib 2.273 には Web Functions の L1(CfnWebFunction など)しかありません。そこで、関数・リビジョン・エンドポイント・コード置き場の S3・実行ロールをまとめて作る L2 を自作しました。

- リポジトリ:https://github.com/reeschan/cdk-lambda-web (MIT、非公式)
- 初回デプロイで、S3 のキーに関数名のプレフィックスが付いていない不具合が見つかりました。修正済みです。
- CDK CLI は
Unknown resource type AWS::Lambda::WebFunctionと警告を出しますが、CDK 同梱のスキーマが古いだけで、synth もデプロイも通ります。
計測環境

- リージョン:ap-northeast-1(東京)
- 5 アプリと data-api を、それぞれ別の Web Function としてデプロイ
- 計測は自宅の Windows PC から HTTPS で直接
- 直列 100 回(TTFB / 完了時間)
- autocannon で 50 接続 × 15 秒(RPS / p99)
- コールドスタートは、デプロイ直後の最初のリクエスト(各 1 回)
- コストは CloudWatch のメトリクス(
AWS/Lambda/Webの CPU 時間・稼働時間・リクエスト数)に東京の単価を掛けて算出
東京の単価:
| 項目 | 単価 |
|---|---|
| CPU | $0.0000322421 / vCPU 秒 |
| メモリ | $0.0000042688 / GB 秒 |
| リクエスト | $0.30 / 100 万 |
結果
全アプリ・全ルートで、200 以外の応答は 0 件でした。太字は、Next cached(参考値)を除いた各行の最良値です。
| SvelteKit | TanStack | Next | Next CC | Next cached(参考) | |
|---|---|---|---|---|---|
| 詳細 完了 p50 | 342ms | 342ms | 349ms | 340ms | 62ms |
| 一覧 RPS(50 同時) | 678 | 659 | 411 | 537 | 732 |
| 詳細 RPS(50 同時) | 152 | 150 | 138 | 145 | 741 |
| 詳細 p99(50 同時) | 467ms | 496ms | 1,552ms | 439ms | 86ms |
| コールドスタート(1 回) | 1.3s | 0.93s | 2.6s | 1.5s | 1.35s |
| CPU / リクエスト | 5.7ms | 7.7ms | 23.8ms | 27.5ms | 18.7ms |
| 100 万リクエストあたり(CPU + リクエスト料金) | $0.48 | $0.55 | $1.07 | $1.19 | $0.90 |
※ Next cached はキャッシュありなので参考値です。
- 詳細の約 340ms は、ほぼ data-api の待ち時間です。直列計測での最初のバイトは各アプリ約 90ms(表には載せていません)で、ストリーミングは Lambda 上でも効いています。
- 詳細ページの RPS(138〜152)は、どれも上限(約 150)に張り付いています。 なのでこの数字からは、フレームワークの差は読み取れません。Lambda が環境を増やして差を吸収した、というより、この計測では差が出ようがなかった、と読むのが正確です。
- 一覧ページでは差が残りました。 Next は 411 RPS で SvelteKit(678)の約 6 割です。Node コンテナでの 1/5 よりは縮まっています。一覧ページの応答 p50 を見ると、SvelteKit / TanStack は負荷中も 72〜73ms でした。素の Next だけは、直列計測(負荷なし)の 85ms から負荷中は 120ms に伸びていて、それがそのまま RPS の差になっています(どのアプリも RPS ≈ 50 ÷ 負荷中の応答 p50 でした)。
- CPU の差は、CPU 時間とコストにはっきり出ます。 1 リクエストあたりの CPU 時間は SvelteKit の 3.3〜4.8 倍で、リクエスト料金を含めても Next 系は約 2 倍のコストでした。
- p99 は素の Next だけ 1.5 秒台まで悪化しました。一方、Next CC は 439ms で、参考値の Next cached を除く 4 つの中で一番良い値です。Node コンテナでは Next CC の方が悪かったので、逆の結果になりました。原因は確かめていません(各 1 回の計測です)。
- Next cached の詳細 741 RPS は、キャッシュで 1 回の応答が約 62ms と短く、上限が約 800 まで上がったためです。CPU が速いわけではありません。
- コールドスタートは Next が最も遅く 2.6 秒、TanStack は 1 秒を切りました(各 1 回の計測です)。
コストで気をつけたいこと:メモリ課金
今回の計測全体の費用は 約 $0.96 でした。その 約 9 割はメモリです。
- 50 同時接続の負荷で、実行環境が 50〜60 個まで増えた
- 負荷が止まったあとも、環境は 6 分以上アイドルのまま残り、その間もメモリが課金された
短いバーストを打つと、増えた環境の「後片付け待ち」の時間が費用の大半を占めます。上の表の「100 万リクエストあたり」は、環境が立っている状態で、リクエストに比例して増える分だけを出した値です。実際の費用は、トラフィックの形(常時あるか、スパイクか)で大きく変わります。
Lambda Web Functions を選ぶメリット
※ この節は今回の計測ではなく、料金表やドキュメントにもとづく一般的な比較です。
コスト:無料ではないが、リクエスト数に対しては安い
Vercel や Cloudflare などには無料枠のあるホスティングがあり、小さなサイトなら 0 円で始められます。ただし無料枠には、商用利用やリクエスト数・実行時間の上限に条件があることが多いです。
Lambda Web Functions は無料ではありません。それでも、今回の計測から出した 100 万リクエストあたりの費用(CPU + リクエスト料金)は次のとおりで、リクエスト数に対してはかなり安く済みます。
| SvelteKit | TanStack | Next | |
|---|---|---|---|
| 100 万リクエストあたり | $0.48 | $0.55 | $1.07 |
I/O 待ちの多いページほどリクエストあたりの CPU 代は安くなりますが、それとは別に、環境が動いている間のメモリ代がかかります。1 つの環境が 1 か月動き続けた場合、消費メモリが 0.5GB なら約 $5.5、2GB 全部なら約 $22 です(東京の単価で計算)。なお、メモリの単価だけを見ると、Web Functions が他のサービスより安いとは限りません。今回の計測でも、費用の大半はメモリでした。「リクエストあたりは安く、アクセスの波が小さいサイトほど効く」と考えるのがよさそうです。小さく始めるなら無料枠のあるホスティングが有利で、アクセスが増えてきたときや AWS の他のサービスとつなぎたいときに Web Functions が効いてきます。
CloudFront と WAF を前段に置ける

AWS の中で動くので、前段に CloudFront を置く構成が取れます。今回の計測ではエンドポイントに直接アクセスしましたが、本番で使うならこの形がおすすめです。
- アプリに守りのロジックを書かなくてよい:IP の許可・拒否や国ごとの振り分けは、CloudFront と CloudFront Functions で済みます。アプリ側に自前の IP フィルターを持つ必要がありません。
- WAF で外からの攻撃に強くなる:AWS WAF を CloudFront につなぐと、レート制限や、SQL インジェクションなどを防ぐマネージドルールが使えます。
- キャッシュでコストも下がる:JS や CSS、画像などの静的ファイルをエッジから返せば、Web Function まで届くリクエストが減り、CPU とリクエストの課金も減ります。
注意点が 1 つあります。CloudFront を置いても、Web Function のエンドポイントに直接アクセスされると前段を素通りされます。CloudFront の開発者ガイドには、エンドポイントを IAM 認証(IamAuth)にして、OAC(Origin Access Control)で「この CloudFront からのアクセスだけ許す」ようにする手順が載っています(Restrict access to an AWS Lambda Web Function origin)。今回の計測では、この構成までは試していません。
CloudFront には Shield Standard の DDoS 防御が最初から付いていて、国単位のアクセス制限もできます。
まとめ
- 1 リクエストの速さは、どれを選んでもほぼ同じでした。I/O 待ちが支配的なページでは、フレームワークの差は 10ms 程度です。
- CPU の重さは Next.js が明確に大きいです。Node コンテナでは RPS の差(第 1 回で SvelteKit の 1/3〜1/5)として、Lambda Web Functions では CPU 時間とコストの差(約 2 倍)として現れました。p99 とコールドスタートでも、素の Next が一番重い結果でした。
- SvelteKit と TanStack Start はかなり近いです。CPU、コスト、メモリは SvelteKit がやや有利で、コールドスタートは TanStack が速い結果でした。
- Cache Components は TTFB を劇的に縮めます(60ms → 5ms)。ただし、データキャッシュなしだと中身が届く時間は変わらず、CPU 1 コアのコンテナではスループットと p99 がむしろ悪化しました。
- Lambda Web Functions は、既存の Node サーバーをほぼそのまま載せられて便利でした。リクエスト数に対しては安く、CloudFront や WAF を前段に置けるのも強みです。ただしバースト時は、環境が増えたぶんのメモリ課金が効いてきます。
注意・まだ見ていないこと
- 計測は各条件 1 回ずつで、ネットワークも自宅回線です。数値は傾向として見てください。
- 負荷テストは 50 接続固定で、詳細ページは計測方法の上限に張り付いています。接続数を増やした計測はしていません。
- Lambda のコストは、CloudWatch の
EnvironmentCPUTimeを課金対象の vCPU 時間とみなして計算しています。AWS のドキュメントでは確認できていない仮定です。 - 未計測:Core Web Vitals(Lighthouse)、SvelteKit / TanStack 側のキャッシュ機構を使った版、遅い API が増えた場合、常時トラフィックでのコスト。
使ったバージョン
- SvelteKit 3.0.1(Svelte 5.57、Vite 8)
- TanStack Start:
@tanstack/react-start1.169.2。「1.0」は安定版リリースの名前で、npm パッケージの版番号は 1.x のまま上がっています - Next.js 16.4.0
- Node.js 24(Lambda は nodejs24.x / arm64)
- aws-cdk-lib 2.273.0