Cloudflare Workers vs AWS Lambda コスト比較: Flutterバックエンド、月100万〜1億リクエスト帯の実測

Flutterアプリのバックエンドとしてサーバーレスを検討しながら「Cloudflare Workers vs AWS Lambda コスト比較」を検索したことがあるなら、大半の記事が「Workersの方が安い」とか「Lambdaのエコシステムの方が大きい」といったざっくりした結論で終わっているのを目にしたはずだ。実際には**ワークロードの性質とトラフィック規模によって勝者が入れ替わる。**この記事では、Flutterアプリでよく作られる3つのユースケース——プッシュ配信サーバー(FCM/APNs呼び出し)、Webhookハンドラー(RevenueCat・Stripe・App Store Server Notificationsの検証)、汎用APIサーバー——を基準に、両プラットフォームの課金構造の違いとAPI Gatewayの追加コストまで反映して、月100万/1,000万/1億リクエスト帯の実際の請求額を直接計算した。
要点まとめ
- Cloudflare WorkersはCPU使用時間だけを課金し、外部APIの応答を待つ待機時間は料金に含まれない。AWS Lambdaは待機時間まで含めた実行時間全体(wall-clock) をメモリ容量に比例させて課金する。
- Lambdaのリクエスト数+実行時間は月100万件 + 40万GB秒が永久無料(Always Free)だ。Workersの無料プランは1日10万件(月約300万件)、呼び出しあたりCPU 10msまでしか無料ではなく、このCPU上限を超えると最低$5/月のPaidプランへの移行が必要になる。
- LambdaをHTTPで公開する際、API Gatewayを付けるとHTTP API基準で100万件あたり$1.00、REST APIは$3.50が別途かかる。 単一エンドポイントならLambda Function URLでこのコストを完全になくせる。Workersはルーティングがランタイムに組み込まれているため、そもそもこの項目自体が存在しない。
- この記事の前提(リクエストあたりwall-clock 300ms、実CPU 20ms、メモリ256MB)では、API Gatewayを使う場合は月約550万件、Function URLを使う場合は月約1,100万件を境にWorkersの方が安くなる。それより下の帯域ではLambdaが有利だ。
- 逆に画像リサイズのようなCPU演算型の関数は、低メモリ(256MB)設定を基準にすると全帯域でLambdaが有利だった。ただしCPUパワー確保のためにメモリを1.1GB〜1.4GB以上まで上げる必要がある重い処理では、この優位性は薄れる。
課金方式の根本的な違い: CPU時間 vs 実行時間全体
両プラットフォームを比較する上でまず理解すべきは、「何を時間として計測して課金するか」 が違うという点だ。
Cloudflare Workersは、Cloudflare公式の料金ドキュメントによればリクエスト数とCPU時間だけが課金対象だ。Workerがfetchで外部APIを呼び出して応答を待っている間は、V8アイソレートが実際に命令を実行しているわけではなく待機状態にあるため、この区間は**請求書に一切反映されない。**外部APIを5回連鎖呼び出しして全体の応答まで2秒かかったとしても、その中で実際にJSコードが実行された時間(パース、シリアライズ、条件分岐など)が20msなら、課金されるのはちょうど20msだけだ。呼び出しあたりのデフォルトCPU上限は30秒で、Paidプランでは設定により最大5分(300,000ms)まで延長でき、Cron Trigger・Queueコンシューマーは最大15分まで許容される。
AWS Lambdaは逆に、「ハンドラーコードが開始してから応答を返すまでの時間全体」 をwall-clock基準で計測し、これを関数に割り当てたメモリ容量(GB) と掛け合わせてGB秒単位で課金する。外部APIの応答を待っている間もその実行環境はずっと「実行中」として計上されるため、この待機時間もそのまま料金になる。つまり同じWebhookハンドラーでも、外部APIが250msで応答しようが800msで応答しようが、Lambdaにとってはその差がそのままGB秒コストの差に直結する。Lambdaの実行時間は1ms単位で切り上げられ、関数のタイムアウトは最大900秒(15分)、メモリは128MB〜10,240MBの間で1MB単位で設定できる。ちなみに1,769MBの地点がvCPU 1個分に相当する計算能力として知られており、CPUを多く使う関数はこの近辺かそれ以上までメモリを上げてデプロイするのが一般的だ。
この構造の違いが生む実務的な結論はシンプルだ。**外部API・DBの応答を待つ時間が長いワークロード(I/O待機型)ほどWorkersが有利になり、純粋な演算時間が長いワークロード(CPU演算型)ほど両プラットフォームの差が縮まるか逆転する。**Flutterバックエンドでよく作られるプッシュ配信サーバー(FCM HTTP v1 API呼び出し)やWebhookハンドラー(署名検証後に外部APIを呼び出す)は典型的なI/O待機型だ。
同じロジック、異なるコードの骨格
実際に両プラットフォームへデプロイするコードはほとんど同じ形をしている。以下はRevenueCat/Stripe系のWebhookを受け取り、署名を検証してFCMでプッシュを送信するハンドラーを、HonoでWorker向けに書いたバージョンだ。
// Cloudflare Worker (Hono)
import { Hono } from "hono";
type Bindings = { WEBHOOK_SECRET: string; FCM_PROJECT_ID: string };
const app = new Hono<{ Bindings: Bindings }>();
app.post("/webhooks/push", async (c) => {
const rawBody = await c.req.text();
const signature = c.req.header("X-Signature") ?? "";
if (!(await verifyHmacSignature(rawBody, signature, c.env.WEBHOOK_SECRET))) {
return c.text("invalid signature", 401);
}
const event = JSON.parse(rawBody);
const accessToken = await getFcmAccessToken(c.env); // 서비스 계정 JWT 교환
await fetch(
`https://fcm.googleapis.com/v1/projects/${c.env.FCM_PROJECT_ID}/messages:send`,
{
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ message: buildFcmMessage(event) }),
},
);
return c.json({ ok: true });
});
export default app;
Lambda + Function URLに移植すると、トリガーの方式が変わるだけでロジックはそのままだ。
// AWS Lambda (Node.js, Function URL 트리거)
import type { APIGatewayProxyEventV2, APIGatewayProxyResultV2 } from "aws-lambda";
export const handler = async (
event: APIGatewayProxyEventV2,
): Promise<APIGatewayProxyResultV2> => {
const rawBody = event.body ?? "";
const signature = event.headers["x-signature"] ?? "";
if (!(await verifyHmacSignature(rawBody, signature, process.env.WEBHOOK_SECRET!))) {
return { statusCode: 401, body: "invalid signature" };
}
const payload = JSON.parse(rawBody);
const accessToken = await getFcmAccessToken();
await fetch(
`https://fcm.googleapis.com/v1/projects/${process.env.FCM_PROJECT_ID}/messages:send`,
{
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ message: buildFcmMessage(payload) }),
},
);
return { statusCode: 200, body: JSON.stringify({ ok: true }) };
};
コードレベルの開発体験は事実上同じだ。この記事で扱う違いは、丸ごと**「このリクエスト1つを処理するのにかかった時間を誰がどう計測して課金するか」**という問題だ。
無料枠の比較: 太っ腹に見えて実は罠がある両社の無料ティア
| 項目 | Cloudflare Workers (Free) | AWS Lambda (Always Free) |
|---|---|---|
| リクエスト上限 | 10万件/日(月約300万件) | 100万件/月 |
| コンピューティング上限 | 呼び出しあたりCPU 10ms | 40万GB秒/月 |
| 待機時間の課金 | なし(そもそも対象外) | あり(wall-clockに含まれる) |
| 有効期間 | 永久無料 | 永久無料(新規アカウント向け12ヶ月限定クレジットとは別枠) |
| 上限超過時 | Paidプラン($5/月〜)への移行が必要 | 超過分から従量課金 |
数字だけを見るとLambdaの「月100万件 + 40万GB秒永久無料」の方がはるかに気前よく見えるが、**Workers Freeプランの本当の罠はリクエスト数ではなく「呼び出しあたりCPU 10ms」という上限だ。**単純なJSONパース・応答程度なら10ms以内に収まるが、ここにHMAC署名検証、Zodのようなスキーマ検証、JWTデコードまで加わると、実務コードは思いのほか簡単に10msを超えてしまう。この上限を超えるとリクエスト自体がCPU超過エラーで打ち切られるため、実際に何らかのロジックがあるAPI・Webhookハンドラーであれば、事実上Paidプラン$5/月が強制されると考えた方がいい。逆にLambdaはメモリと時間を余裕を持って設定しても、free tier内に収まるトラフィックであれば本当に$0で運用できる。
API Gatewayの落とし穴: Lambdaのルーティングには隠れた請求書がついてくる
Lambda関数そのものはHTTPを直接理解できない。ブラウザやFlutterアプリからHTTPSで呼び出すには前段に何かを付ける必要があるが、この選択が総コストに思いのほか大きな影響を与える。
- API Gateway (HTTP API): 100万件あたり**$1.00**(最初の3億件区間)、それ以降は100万件あたり$0.90。カスタムドメイン、複数のLambdaをパス別にマッピングするルーティング、JWT/Lambdaオーソライザー、使用量プラン(APIキー・クォータ)などをサポートする。
- API Gateway (REST API): 100万件あたり**$3.50**(最初の3.33億件区間)。WAF統合、リクエスト/レスポンス変換、キャッシングなどより高度な機能を持つが、その分高い。
- 12ヶ月限定の無料ティア: 新規AWSアカウントはHTTP API・REST APIそれぞれ月100万件まで12ヶ月間無料だ。この記事の計算はその期間が終わった通常課金状態を基準にしている。
- Lambda Function URL: 関数1つに専用のHTTPSエンドポイントを1つ無料で付けられる機能だ。**追加課金は一切なく、純粋にLambdaのリクエスト・実行時間の料金だけが適用される。**代わりに関数1つにつきURL1つ(パスベースで複数のLambdaを1つのAPIにまとめることは不可能)で、APIキー・使用量プラン・WAF統合・リクエスト検証といった管理機能はない。
Flutterアプリのwebhookハンドラーやプッシュ配信サーバーは通常、エンドポイント1つ(または少数)で十分な単一目的の関数だ。こうした場合はAPI Gatewayのルーティング・使用量管理機能がそもそも不要なので、**Function URLで十分だ。**逆に複数のLambda関数を1つのAPIの下にパス別にまとめ、外部パートナーにAPIキーを発行してクォータを管理する必要がある汎用APIサーバーであれば、API Gatewayが必要になる。
Cloudflare Workersにはこの区別自体が存在しない。wrangler.tomlのルート設定やWorkerコード内のルーター(Hono、itty-routerなど)でいくら多くのパスを処理しても、リクエストあたりの課金は同一だ。
# wrangler.toml — Workers는 라우팅에 별도 과금 항목이 없다
name = "push-webhook-worker"
main = "src/index.ts"
compatibility_date = "2025-01-01"
routes = [
{ pattern = "api.example.com/webhooks/*", zone_name = "example.com" },
{ pattern = "api.example.com/push/*", zone_name = "example.com" }
]
トラフィック帯域別の請求額シミュレーション: 月100万/1,000万/1億リクエスト
以下の計算は次の前提に基づく。実際の値はサービスごとに異なるので、後述の計算式に直接代入して確認することを勧める。
- Lambdaメモリ256MB(Webhook・軽量APIでよくある設定)、アーキテクチャはx86_64基準
- シナリオA(I/O待機型) — Webhookハンドラー・プッシュ配信サーバー: リクエストあたりwall-clock 300ms、そのうち実際のCPU使用20ms(残り280msは外部APIの応答待ち)
- シナリオB(CPU演算型) — 画像リサイズ・PDF生成など: リクエストあたりwall-clock 200ms、CPU使用180ms(待機ほぼなし)
- Lambda側はAPI Gatewayの12ヶ月無料ティアが終わった通常課金状態を基準とし、HTTP API(REST APIより安い方)で計算
シナリオA: I/O待機型(Webhookハンドラー、プッシュ配信サーバー)
| 月間リクエスト数 | Workers (Paid $5〜) | Lambdaコンピューティングのみ | Lambda + Function URL | Lambda + API Gateway(HTTP API) |
|---|---|---|---|---|
| 100万 | $5.00 | $0.00 | $0.00 | $1.00 |
| 1,000万 | $8.40 | $7.63 | $7.63 | $17.63 |
| 1億 | $71.40 | $138.13 | $138.13 | $238.13 |
シナリオB: CPU演算型(画像リサイズ、署名・暗号化など)
| 月間リクエスト数 | Workers (Paid $5〜) | Lambdaコンピューティングのみ | Lambda + Function URL | Lambda + API Gateway(HTTP API) |
|---|---|---|---|---|
| 100万 | $8.00 | $0.00 | $0.00 | $1.00 |
| 1,000万 | $40.40 | $3.47 | $3.47 | $13.47 |
| 1億 | $391.40 | $96.47 | $96.47 | $196.47 |
2つの表を並べて見ると興味深い点が見えてくる。**シナリオA(I/O待機型)では低トラフィック帯ではLambdaが圧倒的に安いが、トラフィックが増えるにつれてWorkersが逆転する。逆にシナリオB(CPU演算型)では、この256MBという前提を基準にすると全帯域でLambdaの方が安い。**次のセクションでその理由と正確な損益分岐点を確認する。
ワークロードの性質によって勝者が変わる理由
計算に使った計算式をそのまま公開する。自分のワークロードの数値を代入して、実際に再計算してみることができる。
Workers 월 비용(USD) = 5
+ max(0, 요청수_백만 - 10) × 0.30
+ max(0, 요청당_CPU_ms × 요청수_백만 - 30) × 0.02
Lambda 컴퓨팅 비용(USD) = max(0, 요청수_백만 - 1) × 0.20
+ max(0, GB초_총합 - 400,000) × 0.0000166667
GB초_총합 = 요청수 × 메모리(GB) × 평균_wall_clock_초
この計算式にシナリオAの数値を代入して2本の曲線が交わる点を解くと、コンピューティングコストだけを比較した場合、月約1,100万件でWorkersがLambdaを追い抜き始める。ここにAPI Gateway(HTTP API、100万件あたり$1.00)を加えると、Lambda側のコスト曲線の傾きが急になり、損益分岐点が月約550万件へとほぼ半分にまで前倒しされる。つまり「API Gatewayを使うかFunction URLを使うか」という選択一つが、CPU時間課金 対 wall-clock課金という根本的な違いに劣らず総コストに影響を与える。
シナリオB(CPU演算型)でLambdaが勝ち続ける理由は単純だ。256MB(0.25GB)という低いメモリ設定ではGB秒単価が非常に小さく(リクエストあたり0.2秒 × 0.25GB × $0.0000166667 ≈ $0.00000083)、WorkersのCPU-ms単価(リクエストあたり180ms × $0.02/100万ms ≈ $0.0000036)よりもむしろ安くなるからだ。しかしこの結果はメモリ256MBという前提に強く依存している。同じ計算式で逆算すると、メモリをおおよそ1.1GB〜1.4GB以上まで上げる必要があるワークロード(実際にsharpのようなライブラリで画像リサイズを行う際、十分なCPUパワーを確保するために512MB〜1,769MBを使うケースは珍しくない)では、この優位性は薄れるか消え、トラフィックが大きくなるほど再びWorkers側に天秤が傾く。「CPU演算型だから無条件でLambda」と決めつける前に、実際にデプロイするメモリ設定を代入して確認すべき理由がここにある。
見落としがちな追加コスト: サブリクエスト上限とデータ転送料
プッシュ配信サーバーはリクエスト1つあたりFCM・APNsのような外部APIを複数回呼び出す構造が多い。Cloudflareはこれを「サブリクエスト」として別途課金することはないが(リクエスト数に計上されるのはインバウンドリクエスト1つだけ)、同時に発行できるサブリクエスト数には上限がある。公式ドキュメント基準で**Freeプランは50件/リクエスト、Paidプランはデフォルト1,000件/リクエスト(2026年2月の変更以降デフォルト値が引き上げられ最大10,000件まで、設定により最大1,000万件まで拡張可能)**だ。ファンアウトで数百個のデバイストークンへ個別にリクエストを飛ばす構造なら、この上限を事前に確認しておく必要がある。
もう一つ実務でよく見落とされる項目がデータ転送(egress)コストだ。AWSはリージョン外へ出ていく応答トラフィックにGBあたり$0.09が別途かかる(API Gateway・Lambda共通で適用される一般的なAWSデータ転送料金)。Cloudflare Workersの料金体系にはこうした別枠の応答egress課金項目自体が存在しない。応答ペイロードが大きくないWebhook・プッシュのユースケースでは些細な差だが、大きなJSONを返すAPIサーバーであれば、トラフィックが大きくなるほどこの項目も積み重なっていく。
結論: Flutterのプッシュ配信サーバー・Webhookハンドラーには何を使うべきか
- 月100万件以下の初期〜中小規模: Lambda + Function URLが事実上$0に近い(リクエスト・実行時間ともに永久無料ティアの範囲内に収まる)。WorkersはPaidプランの最低$5が固定費として発生するため、この帯域ではLambdaが有利だ。ただし実際のCPU使用量が呼び出しあたり10msを超えない本当に軽いロジックであれば、Workers Freeプラン(月約300万件まで無料)でも$0を維持できる。
- 月500万〜1,100万件の帯域(この記事の前提基準): ルーティング方式によって勝者が分かれる。API Gatewayを付ける場合はすでにこの帯域からWorkersが優勢になり、Function URLだけで十分な単一エンドポイント構造なら1,100万件まではLambdaがわずかに有利だ。
- 月1,000万件を超えるスケール(大規模な定期プッシュキャンペーン、大量のWebhookファンアウト): I/O待機型のワークロードであればほぼ常にWorkersが有利だ。CPU時間だけを課金する構造が、待機時間の長いワークロードにおいて本質的に有利だからだ。
- 画像リサイズのように本当にCPUを多用する関数: 低メモリ(256MB〜512MB)設定であれば、どのスケールでもLambdaが有利なケースが多い。ただし十分なCPUパワーを確保するためにメモリを1.1GB以上まで上げる必要があるなら、この優位性は薄れる。
- ルーティングレイヤーを必ず先に点検せよ。 LambdaをAPI GatewayなしでFunction URLとして公開できるかどうかを確認するだけで、損益分岐点がほぼ2倍(約550万件 → 約1,100万件)先延ばしになる。これはCPU課金かwall-clock課金かという構造的な違いよりも即座に適用できる、最も手軽なコスト削減ポイントだ。
結局のところ「Cloudflare Workers vs AWS Lambda コスト比較」に唯一の正解はない。トラフィック規模、リクエストあたりの待機時間とCPU時間の比率、そしてルーティングレイヤーの選択——この3つの変数をこの記事の計算式に直接代入してみることこそが、唯一正確な答えだ。