Cloudflare Pages + R2でプレビューデプロイ時に「R2 bucket not found」エラーが発生していませんか?

本番環境では成功するのに、PRからのプレビュー環境だけ失敗する——

この問題の原因はwrangler.tomlpreview_bucket_name設定にあります。

この記事では、エラーの原因と2つの解決方法を解説します。
設定の修正だけで解決できるので、5分程度で問題を解消できます。

こんな人におすすめ

この記事は以下のような方に役立ちます。

  • Cloudflare Pages + R2 で画像やファイルを保存する機能を実装している
  • PRからのプレビューデプロイで「R2 bucket not found」エラーが出て困っている
  • wrangler.toml の環境設定の書き方がよくわからない
  • 個人開発〜小規模チームでCloudflareを使っている

目次

背景

Cloudflare R2とは

Cloudflare R2は、Cloudflareが提供するオブジェクトストレージサービスです。
AWS S3互換のAPIを持ちながら、エグレス(データ転送)料金が無料という特徴があります。

画像やファイルの保存先として、Cloudflare Pagesと組み合わせて使うケースが増えています。

今回の状況

Cloudflare Pages + R2 を使った画像保存機能を実装していたときのことです。

PRを作成してCloudflareにデプロイしようとしたら、プレビュー環境でエラーが発生しました。

正直なところ、このエラーの原因を特定するのに30分ほど悩みました。

エラーメッセージと症状

Cloudflare Pagesのビルドは成功しますが、Functions(Workers)のデプロイ時に以下のエラーでデプロイが失敗します。

Error: Failed to publish your Function. Got error: R2 bucket 'my-project-storage-preview' not found. Please use a different name and try again.

意外にも、mainブランチへの直接デプロイ(本番環境)では問題なく成功します。

PRからのデプロイ(プレビュー環境)でのみ発生するのがポイントです。

つまづいた点と原因

問題のある設定

私がやらかしていたのは、wrangler.toml の R2 バケット設定で、存在しないバケット名を指定していたことです。

# 問題のある設定
[[r2_buckets]]
binding = "STORAGE"
bucket_name = "my-project-storage"
preview_bucket_name = "my-project-storage-preview"  # ← このバケットが存在しない

preview_bucket_nameを設定しておけば、開発環境と本番環境を分離できるだろう」と安易に考えていました。

しかし、実際にはそのバケットをCloudflare R2に作成していませんでした。

なぜエラーが発生したのか

ここで私が理解していなかったのは、preview_bucket_nameenv.previewは役割が異なるということです。

設定 用途 使用される場面
preview_bucket_name ローカル開発用のバケット名 wrangler devwrangler dev --remote
env.preview プレビュー環境の設定全体 Cloudflare Pagesのプレビューデプロイ

公式ドキュメントによると、preview_bucket_nameは主にwrangler dev(ローカル開発)で使用される設定です。

しかし、Cloudflare Pagesでは、PRからのデプロイ時にプレビュー環境としてpreview_bucket_nameが参照される挙動になっていました。

結果として、Cloudflare R2に実際には作成していないmy-project-storage-previewバケットを参照しようとしてエラーになりました。

この仕様を理解するまでに時間がかかりました。

解決方法

2つのアプローチがあります。

プロジェクトの規模や要件に応じて選択してください。

方法1: プレビュー用バケットを実際に作成する(推奨)

本番データとプレビュー環境のデータを分離したい場合は、この方法がベストです。

手順

  1. Cloudflare Dashboardで新しいR2バケットを作成

    • バケット名: my-project-storage-preview(wrangler.tomlで指定した名前)
  2. wrangler.tomlの設定はそのまま維持します

[[r2_buckets]]
binding = "STORAGE"
bucket_name = "my-project-storage"
preview_bucket_name = "my-project-storage-preview"  # 実際に作成したバケット

# プレビュー環境用の設定
[env.preview]
[[env.preview.r2_buckets]]
binding = "STORAGE"
bucket_name = "my-project-storage-preview"

この方法のメリット:

  • 本番データとプレビューデータが完全に分離されます
  • チーム開発で安全にテストできます
  • 誤って本番データを変更するリスクがありません

デメリット:

  • バケットが2つ必要(管理オーバーヘッドがあります)
  • R2のストレージコストが増加する可能性があります

方法2: 本番バケットを共用する

個人的には、小規模プロジェクトや個人開発ならこちらで十分だと感じています。

手順

wrangler.tomlを修正し、プレビュー環境でも本番バケットを使用します。

[[r2_buckets]]
binding = "STORAGE"
bucket_name = "my-project-storage"
preview_bucket_name = "my-project-storage"  # ← 本番と同じバケットを使用

# プレビュー環境用の設定も同様に修正
[env.preview]
[[env.preview.r2_buckets]]
binding = "STORAGE"
bucket_name = "my-project-storage"

さらに、プレビュー環境の環境変数も本番設定に合わせて更新します。

  • R2_PUBLIC_URL: r2.dev公開URLを設定
  • 関連するAPI URLやオリジン設定

この方法のメリット:

  • 設定がシンプルです
  • バケット管理のオーバーヘッドがありません
  • コストを抑えられます

デメリット:

  • 本番データに影響を与える可能性があります
  • チーム開発では注意が必要です

良かった点・得られた効果

この問題を解決したことで、以下の効果がありました。

即時の効果:

  • PRからのプレビューデプロイが5分程度で成功するようになりました
  • チームメンバーがPRをレビューする前に実際の動作を確認できるようになりました

長期的なメリット:

  • 実際に設定を見直してみると、Cloudflare Pagesの環境設定について深く理解できました
  • 今後同様の問題が発生しても、すぐに原因を特定できます
  • wrangler.tomlの書き方に自信がつきました

ベストプラクティス

プロジェクト規模別の推奨設定

プロジェクト 推奨方法 理由
個人開発・小規模 方法2(バケット共用) シンプルさ優先
チーム開発・中規模以上 方法1(バケット分離) 安全性優先
本番データが重要 方法1(バケット分離) リスク回避

注意点:ノンインヘリタブルキーの制約

Cloudflare Pages のドキュメントによると、r2_bucketsはノンインヘリタブルキーに分類されます。

env.previewenv.productionでこれらの設定を一つでもオーバーライドする場合、すべてのノンインヘリタブルキーを環境設定で再定義する必要があります

これを知らないと、また別のエラーでハマることになります。

# 正しい設定例:すべてのノンインヘリタブルキーを再定義
[env.preview]
[[env.preview.r2_buckets]]
binding = "STORAGE"
bucket_name = "my-project-storage-preview"

[[env.preview.kv_namespaces]]  # KVも使っている場合は再定義が必要
binding = "MY_KV"
id = "xxxx"

よくある質問(FAQ)

Q. preview_bucket_nameとenv.previewの違いは?

preview_bucket_nameはローカル開発(wrangler dev)用、env.previewはCloudflare Pagesのプレビューデプロイ用の設定です。
Cloudflare Pagesでは両方が参照されるため、どちらも正しく設定する必要があります。

Q. バケットを分離すべきか共用すべきか?

個人開発・小規模プロジェクトなら本番バケット共用(方法2)で十分です。
チーム開発・本番データが重要な場合はバケット分離(方法1)を推奨します。

Q. ノンインヘリタブルキーとは?

r2_bucketskv_namespacesなど、環境設定で一部をオーバーライドするとすべて再定義が必要になる設定項目のことです。
一つでも漏れるとエラーになるため注意が必要です。

まとめ

今回の問題から得た教訓をまとめます。

  1. preview_bucket_nameenv.previewは別物

    • ローカル開発用とCloudflare Pagesのプレビュー環境用で、設定の役割が異なります
  2. 存在しないリソースを参照しない

    • wrangler.tomlで指定するバケット名は、実際にCloudflare R2に存在するものを使います
  3. 環境分離の判断

    • プロジェクトの規模や重要度に応じて、バケットを分離するかどうかを判断します
  4. ノンインヘリタブルキーの制約

    • 環境設定でオーバーライドする場合は、すべての関連設定を再定義します

参考リンク