Cloudflare R2 を使った画像ストレージ構成

WEB職

― ブラウザ直PUTから本番運用まで、つまずかない完全ガイド ―

Web写真集プラットフォーム「PicZine」 では、画像やページデータの保存先として Cloudflare R2 を採用しています。

この記事では、

  • Cloudflare R2 とは何か
  • 「ブラウザから直接 PUT する」とはどういう意味か
  • なぜ CORS 設定が必要なのか
  • 開発環境から本番環境へどう切り替えるのか

といった 初心者が一番つまずきやすいポイントを丁寧に補足しながら、
実際のセットアップ手順を解説します。

「S3互換って聞くけど、結局どう使うの?」
という人を想定しています。


Cloudflare R2 とは?

Cloudflare R2 は、Cloudflare が提供する オブジェクトストレージ です。
Amazon S3 と互換性のある API を持ちながら、次の特徴があります。

  • データ転送料が無料
  • Cloudflare CDN と直結
  • カスタムドメインでそのまま配信可能
  • S3 SDK がそのまま使える

特に画像・動画を扱う Web アプリでは
「保存コストよりも転送料が重くなる」ケースが多いため、
R2 はかなり現実的な選択肢になります。


今回の構成の全体像

まずは完成形のイメージです。

[ブラウザ]
   ↓ PUT(直接アップロード)
[Cloudflare R2]
   ↓ CDN 経由
[images.xxxxxx.com]

ポイントは アプリサーバーを経由しない ことです。


「ブラウザから直接 PUT する」とは?

この記事で何度も出てくる重要な言葉なので、
まずここをしっかり説明します。


よくあるアップロード構成(サーバー経由)

一般的な Web アプリでは、次のような流れが多いです。

[ブラウザ]
   ↓ POST
[アプリサーバー]
   ↓ PUT
[ストレージ]

この場合、

  • サーバーが画像データを一度受け取る
  • サーバーの帯域・CPU・転送料を消費する
  • 同時アップロードが増えるとサーバーが重くなる

という特徴があります。


PicZine で採用している構成(ブラウザ直PUT)

今回採用しているのは、次の構成です。

[ブラウザ]
   ↓ PUT
[Cloudflare R2]

つまり、

  • ブラウザが直接 R2 にファイルをアップロード
  • アプリサーバーは画像データを扱わない
  • サーバーは「メタ情報保存」など最低限の役割のみ

これを 「ブラウザから直接 PUT する」 と呼んでいます。


PUT とは何か?

HTTP にはいくつかのメソッドがあります。

  • GET:取得する
  • POST:処理を依頼する
  • PUT:指定した場所にデータをそのまま置く

R2(S3互換ストレージ)では、

PUT /bucket/path/image.jpg

のように
「このパスに、このファイルを保存する」
という使い方が基本になります。


なぜブラウザ直PUTにするのか?

理由はかなりシンプルです。

① サーバー負荷がほぼゼロ

  • 画像をサーバーで処理しない
  • API は軽い JSON 通信だけ

→ スケールしやすく、構成もシンプル。


② コストが安定する

  • R2 は 転送料が無料
  • Cloudflare CDN から直接配信

画像が増えても
想定外の請求が起きにくいです。


③ フロントエンドだけで完結する

  • React / Vue / Svelte から直接アップロード可能
  • fetch や SDK で実装できる

PicZine のような
クライアント主導のアプリと相性が良い構成です。


でも、誰でも勝手にアップロードできない?

ここで重要になるのが CORSAPI トークン です。


なぜ CORS 設定が必要なのか?

ブラウザにはセキュリティ制限があります。

「この Web サイトが
他のドメインに勝手にデータを送っていいの?」

これを制御する仕組みが
CORS(Cross-Origin Resource Sharing) です。


CORS を設定しないとどうなる?

たとえ正しい API キーを使っていても、

  • ブラウザ → R2 への PUT
  • ブラウザ側で通信がブロック

されます。

そのため、
ブラウザ直PUTを行う場合は CORS 設定が必須です。


開発環境のセットアップ(Development)

ここから実際の手順です。

Step 1. R2 バケットを作成

  1. Cloudflare ダッシュボードにログイン
  2. R2 → Create bucket
  3. バケット名を入力
    例:xxxxxx-pages

Step 2. API トークンを作成

R2 に書き込みを行うための認証情報を作ります。

設定内容:

  • PermissionsAdmin Read & Write
  • TTL:Forever(または任意)

取得した値を .env に設定します。

VITE_R2_ACCESS_KEY_ID=xxxx
VITE_R2_SECRET_ACCESS_KEY=xxxx
VITE_R2_ENDPOINT=https://xxxx.r2.cloudflarestorage.com

※ 開発中は権限を絞らない方が詰まりにくいです


Step 3. CORS を設定する

方法A:スクリプト(推奨)

node scripts/apply-cors.js

方法B:手動設定

Settings → CORS Policy に以下を追加。

[
  {
    "AllowedOrigins": [
      "http://localhost:5173",
      "https://*.pages.dev",
      "*"
    ],
    "AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
    "AllowedHeaders": ["*"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3000
  }
]

Step 4. Public Development URL を有効化

画像表示用の公開 URL を有効にします。

  1. バケットの Settings
  2. Public Development URL → Enable
  3. 表示された URL を .env に設定
VITE_R2_PUBLIC_BASE_URL=https://pub-xxxx.r2.dev

本番環境への切り替え(Production)

Step 1. カスタムドメインを接続

例:images.xxxxx.com

  • Settings → Public Access → Custom Domains
  • Cloudflare 管理ドメインなら DNS はほぼ自動

Step 2. 環境変数を更新

VITE_R2_PUBLIC_BASE_URL=https://images.xxxxxx.com
VITE_STORAGE_PROVIDER=r2

API エンドポイントと公開 URL の違い(重要)

種類用途
r2.cloudflarestorage.comAPI 用(非公開)
xxxx.r2.dev開発用公開 URL
images.xxxxxx.com本番用公開 URL

画像表示には必ず公開URLを使います。


よくあるトラブル

画像が表示されない

VITE_R2_PUBLIC_BASE_URL が API エンドポイントになっていないか確認

CORS エラー

→ ブラウザの Console を確認し、CORS 設定を再確認

AccessDenied

→ API トークンの権限不足(Admin Read & Write で再作成)


まとめ

  • ブラウザ直PUT はサーバーを軽くするための設計
  • CORS はブラウザの安全装置
  • R2 は画像配信と非常に相性がいい
  • 開発は r2.dev、本番はカスタムドメインが基本

この構成を理解しておくと、
画像・動画系の Web アプリ設計が一段楽になります。

コメント

タイトルとURLをコピーしました