Cloudflare Workers 無料プランで 503 が出る原因は CPU 上限
Cloudflare Workers で運用しているサイトが、断続的に 503 を返していました。しかもアクセスが集中していたわけではありません。毎分 10〜31 リクエスト、つまり秒間 0.5 程度の負荷で、4 回に 1 回が失敗していました。
同じサイトが別の時間帯には 1 時間あたり 2,516 リクエストをエラーゼロで捌いているので、負荷では説明が付きません。
原因は無料プランの CPU 時間の上限でした。ただしこの上限は「1 リクエストあたり 10 ミリ秒」という単純な形では適用されず、その仕組みを知らないと原因にたどり着けません。
症状
Cloudflare のログを集計すると、こうなっていました。
2026-08-05T04:40:00Z req= 14 5xx=10 失敗率=71%
2026-08-05T04:44:00Z req= 31 5xx= 1 失敗率= 3%
2026-08-05T05:01:00Z req= 14 5xx= 8 失敗率=57%
合計 req=1035 / 5xx=239 (23.1%) 最大 31 req/分約 45 分間続き、失敗率は 13〜71% の間で揺れました。リクエスト数と失敗率が相関していません。 31 req/分のときに 3%、14 req/分のときに 71% です。
失敗していたのは実在する記事ページで、手動でアクセスすると 200 が返り、失敗は正常な応答の中に断続的に混ざっていました。
外れた仮説
確かめても原因にたどり着かなかった仮説を先に挙げます。
- デプロイ直後の不安定 — その日はデプロイしていませんでした
- Cloudflare 側の障害 — 同じ時間帯、同じアカウントの他 7 サイトは 5xx がゼロでした
- データベースの暴走 — クエリ数は増えていましたが、リクエスト数に比例した増え方で、1 リクエストあたりの本数は平常時と同じでした
- 特定ページの不具合 — 失敗は多数のパスに 2 件ずつ薄く分散していました
- 単純な過負荷 — 前述のとおり、同じサイトがその 3 倍の負荷をエラーゼロで処理しています
この 5 つが外れると、残る仮説は「リクエストごとに与えられた何かの資源を使い切っている」だけになります。
原因は CPU 時間だった
Workers のログを取得して outcome を集計すると、原因がすぐに分かりました。
exceededCpu: 274 件 ← 56%
ok: 216 件
canceled: 1 件
CPU 時間(ms) p50=10 p90=115 p99=221 max=291exceededCpu は Cloudflare が「CPU の上限を超えた」と明示的に記録する値で、CPU 上限の超過はこの記録から確定できます。
無料プランの上限は公式ドキュメントにあるとおり 1 リクエストあたり CPU 10 ミリ秒です。有料プランは既定 30 秒なので、桁が 3 つ違います。
そして Cloudflare のエラー 1102(Worker がリソース上限を超過)は、HTTP 503 として返ります。 例外による 1101 は 500 です。観測された内訳は 503 が 302 件・500 が 2 件で、「大半がリソース上限、まれに例外」という分布と一致していました。
なぜ低負荷でも落ちるのか
上限は 10ms ですが、Cloudflare は一時的な超過を許容し、平均で帳尻を合わせています。
上の実測値では、p90 で 115ms、最大で上限の 29 倍にあたる 291ms を使ったリクエストが成功しており、単発の超過なら処理は通ります。
しかし連続して重いページを処理すると、この余力が枯渇します。 枯渇したあとのリクエストは 10ms で強制終了され、503 になります。
つまり瞬間の負荷ではなく、蓄積した予算の問題です。そのため、次のような一見矛盾した挙動になります。
- 毎分 14 リクエストでも失敗する(余力が尽きているとき)
- 1 時間 2,516 リクエストでも無事(余力が回復しながら処理できているとき)
余力を枯渇させたのはクローラーでした。クローラーはサイトを一巡するために、人間の閲覧と違って間隔を空けずに重いページを連続で要求します。
何が CPU を食っていたか
パスの種類ごとに分けると、こうなりました。
- 記事ページ: 426 件中 234 件が失敗(55%)、CPU 最大 291ms
- カテゴリページ: 49 件中 40 件が失敗(82%)、最大 73ms
- 記事一覧: 10 件中 0 件が失敗、最大 59ms
- トップ: 5 件中 0 件が失敗、最大 76ms
失敗率だけ見るとカテゴリページが目立ちますが、件数・失敗数・CPU 最大値のどれも記事ページが最も大きく、CPU を使っていたのは本文のレンダリングでした。Astro 製の CMS「EmDash」 の Portable Text(構造化データ)を HTML に変換する処理で、記事が長いほど重くなります。
これは 1 サイトの問題ではなかった
同じ構成の全 8 サイトについて、直近 24 時間の CPU 時間を測りました。
- サイト A: 486 リクエスト中 220 件が 10ms 超、p50 7ms、最大 130ms
- サイト B: 519 リクエスト中 186 件が 10ms 超、p50 13ms、最大 269ms
- サイト C: 636 リクエスト中 316 件が 10ms 超、p50 21ms、最大 447ms、`exceededCpu` が 5 件発生済み
- サイト D(今回落ちたサイト): 284 リクエスト中 189 件が 10ms 超、p50 16ms、p90 199ms
全サイトでリクエストの 2〜9 割が 10ms を超えていました。 落ちたサイトはたまたま最初に大きなクロールを受けただけで、サイト C では既に失敗が始まっていました。
測り方
同じ状況を疑うなら、Workers のログを見るのが最短です。ダッシュボードの Workers & Pages から対象の Worker を開き、Logs を見ます。outcome が exceededCpu になっていれば確定です。
API から集計する場合は、observability のクエリ API に $workers.outcome と $workers.cpuTimeMs があります。トークンには Workers Observability の読み取り権限が必要です。
なお無料プランのログ保持は 3 日間です。障害から日が経つと消えるので、気付いたら早めに見てください。
一方で、Cloudflare のアクセス解析(GraphQL Analytics)だけを見ていても原因にはたどり着けません。 そこで分かるのはステータスコードとパスまでで、exceededCpu は出てきません。私は最初アクセス解析だけを見て、負荷やデプロイや障害を疑って時間を使いました。
対処
上限そのものは無料プランでは動かせないので、方向は 2 つです。
1. Worker の実行回数を減らす
エッジキャッシュを設定して、一度描画したページを次から Cloudflare に返させます。キャッシュから返したリクエストは CPU 予算を消費しないので、これを主な対処にします。ただし Cloudflare は HTML を既定ではキャッシュしないため、明示的な設定が要ります。この話はCloudflare は HTML をキャッシュしないに分けて書きました。
2. レンダリングを軽くする
p90 で 115〜199ms かかっているレンダリングを 10ms に収めるのは非現実的で、1 の補助にはなっても単独では上限内に収まりません。
3. 有料プランにする
CPU 上限が 10ms から 30 秒になるので、CPU 上限の超過による 503 は起きなくなります。月 5 ドルです。ただし「毎リクエストで全部描き直している」という非効率は残るので、キャッシュは別途やる価値があります。
私は無料プランのまま実行回数を減らす 1 を選びました。
追記: キャッシュを入れても書き込みは守られない
エッジキャッシュを 8 サイトに入れたあと、記事本文を一括で書き換える作業をしました。57 記事に対して更新と公開を連続で流したところ、CPU 上限超過が 115 件出ました。
直近 3 時間: Worker 実行 1,793 件 / CPU 上限超過 115 件(すべて失敗)
CPU 時間の中央値: 10ms ちょうど(上限で頭打ち)キャッシュから返るのは読み取りのリクエストだけです。管理画面と API は毎回サーバーまで届くので、上限に当たる条件は導入前と変わりません。実際、更新が 2 回、公開が 6 回、途中で 503 になりました(いずれも再実行で成功しています)。
一括処理で上限に達するのは、CPU の予算が時間あたりの平均で回復する仕組みだからです。1 本ずつ手で編集している限り予算は回復しますが、数十本を続けて流すと回復が追いつきません。
落ちるのは書き込み処理だけで公開中のページには影響せず、失敗したリクエストはやり直せば通ります。ただし一括処理では次の 2 点が要ります。
- 一括処理はリトライ前提で組む。1 回の失敗で全体を止めない
- 何件成功したかを必ず数える。成功前提で進めると、途中まで書き換わった状態に気づけない
同じ期間の公開ページ側の CPU 上限超過は 8 サイトすべてで 0 件で、キャッシュは読み取りのリクエストに対しては機能しています。
無料プランで運用するなら
今回のことで、無料プランの CPU 上限は「めったに当たらない上限」ではなく、普通の CMS サイトが日常的に達する上限だと分かりました。記事本文のレンダリングだけで 100ms 台に乗ります。
それでも多くのサイトが落ちていなかったのは、エッジキャッシュが HTML を返していない状態で、たまたまクローラーが連続で来ていなかったからで、運任せの状態でした。
同じ構成で運用しているなら、落ちる前に一度 CPU 時間を測っておくことをお勧めします。p50 が 10ms を超えていたら、いつ落ちてもおかしくありません。
無料プランの他の上限についてはCloudflare Workers 無料プランの 3 MB 上限に、商用利用の可否についてはCloudflare Pages の無料プランは商用利用できるに書いています。