ブログ一覧

Cloudflare Workers 無料プランで 503 が出る原因は CPU 上限

更新: Web開発者向け

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 倍の負荷をエラーゼロで処理しています

ここまで潰すと、残るのは「リクエストごとに与えられた何かの資源を使い切っている」という筋だけになります。

原因は CPU 時間だった

Workers のログを取得して、outcome を集計したら一発でした。

exceededCpu: 274 件  ← 56%
ok:          216 件
canceled:      1 件

CPU 時間(ms)  p50=10  p90=115  p99=221  max=291

exceededCpu は Cloudflare が「CPU の上限を超えた」と明示的に記録するものです。推測ではありません。

無料プランの上限は公式ドキュメントにあるとおり 1 リクエストあたり CPU 10 ミリ秒です。有料プランは既定 30 秒なので、桁が 3 つ違います。

そして Cloudflare のエラー 1102(Worker がリソース上限を超過)は、HTTP 503 として返ります。 例外による 1101 は 500 です。観測された内訳は 503 が 302 件・500 が 2 件で、「大半がリソース上限、まれに例外」という分布と一致していました。

なぜ低負荷でも落ちるのか

ここが理解の要です。上限は 10ms ですが、Cloudflare は一時的な超過を許容し、平均で帳尻を合わせています。

上の実測値を見ると、p90 で 115ms、最大で 291ms 使ったリクエストが成功しています。10ms の 29 倍です。単発なら通る。

しかし連続して重いページを処理すると、この余力が枯渇します。 枯渇したあとは 10ms で強制終了され、503 になる。

つまり瞬間の負荷ではなく、蓄積した予算の問題です。だから次のような、一見矛盾した挙動になります。

  • 毎分 14 リクエストでも失敗する(余力が尽きているとき)
  • 1 時間 2,516 リクエストでも無事(余力が回復しながら処理できているとき)

引き金はクローラーでした。サイトを一巡するために、重いページを連続で要求してきます。人間の閲覧と違って間隔が空きません。

何が CPU を食っていたか

パスの種類ごとに分けると、こうなりました。

  • 記事ページ: 426 件中 234 件が失敗(55%)、CPU 最大 291ms
  • カテゴリページ: 49 件中 40 件が失敗(82%)、最大 73ms
  • 記事一覧: 10 件中 0 件が失敗、最大 59ms
  • トップ: 5 件中 0 件が失敗、最大 76ms

失敗率だけ見るとカテゴリページが目立ちますが、件数でも失敗数でも 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 を見ます。outcomeexceededCpu になっていれば確定です。

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 秒になるので、この種の問題は構造的に消えます。月 5 ドルです。ただし「毎リクエストで全部描き直している」という非効率は残るので、キャッシュは別途やる価値があります。

私は 1 を選びました。無料プランのまま、実行回数を減らす方向です。

追記: キャッシュを入れても書き込みは守られない

エッジキャッシュを 8 サイトに入れたあと、記事本文を一括で書き換える作業をしました。57 記事に対して更新と公開を連続で流したところ、CPU 上限超過が 115 件出ました。

直近 3 時間: Worker 実行 1,793 件 / CPU 上限超過 115 件(すべて失敗)
CPU 時間の中央値: 10ms ちょうど(上限で頭打ち)

キャッシュが効くのは読み取り側だけです。管理画面と API は毎回サーバーまで届くので、上限に当たる条件は導入前と変わりません。実際、更新が 2 回、公開が 6 回、途中で 503 になりました(いずれも再実行で成功しています)。

なぜ一括処理だと当たるのかというと、CPU の予算が時間あたりの平均で回復する仕組みだからです。1 本ずつ手で編集している限り予算は回復しますが、数十本を続けて流すと回復が追いつきません。

被害がどこで止まるかも書いておきます。落ちるのは書き込み処理そのものなので、公開中のページには影響しません。失敗したリクエストをやり直せば通ります。ただし、

  • 一括処理はリトライ前提で組む。1 回の失敗で全体を止めない
  • 何件成功したかを必ず数える。成功前提で進めると、途中まで書き換わった状態に気づけない

読み取り側については、同じ期間に公開ページ側の CPU 上限超過は 8 サイトすべてで 0 件でした。キャッシュは効いています。効く範囲が読み取りに限られる、というだけの話です。

無料プランで運用するなら

今回のことで、無料プランの CPU 上限は「めったに当たらない上限」ではなく、普通の CMS サイトが日常的に触る上限だと分かりました。記事本文のレンダリングだけで 100ms 台に乗ります。

そのうえで落ちていないサイトが多いのは、エッジキャッシュが効いていないのに、クローラーが連続で来ていないからという、かなり運任せの状態でした。

同じ構成で運用しているなら、落ちる前に一度 CPU 時間を測っておくことをお勧めします。p50 が 10ms を超えていたら、いつ落ちてもおかしくありません。

無料プランの他の上限についてはCloudflare Workers 無料プランの 3 MB 上限に、商用利用の可否についてはCloudflare Pages の無料プランは商用利用できるに書いています。

この記事をシェア