Contents
Google Cloud StorageからCloudflare R2へ移行する理由
本ブログはGoogle Cloud StorageとWP-Statelessを組み合わせて画像をアップロード&表示しています。設定当初は費用がそうでもなかったのですが、最近は転送量コスト(エグレス料金)が毎月500円ほどかかるようになりました。
費用を削減するために、Cloud StorageからCloudflare R2に乗り換えました。R2はデータ転送量が無料です。保存料金も安く、WordPressのメディア保存先を置き換えるだけで毎月のコストを削減できるようなので試してみます。
実際に9月20日にCloudStorageからCloudFlare R2に移行したあとの費用グラフです。

移行した日(9/20)は通信量が増えたため、費用が増加していますが、それ以降はCloudStorageの費用(青い部分)がゼロになっていることがわかります。R2ではClass Bの読み込みが無料枠に収まっているため、費用がかかっていません。
Cloudflare R2の料金体系
2026年9月現在の料金体系は以下の通り。
| 項目 | Class A (書き込み・変更) | Class B (読み込み) |
| 役割 | データを追加・変更・削除・一覧取得 | データを読み取り |
| 主な操作(S3 API) | PUT, POST, COPY, ListObjects など | GET, HEAD など |
| 無料枠(月間) | 100万回 / 月 | 1,000万回 / 月 |
| 超過後の料金 | $4.50 / 100万回 | $0.36 / 100万回 |
Cloudflareのキャッシュを有効にしておけば、キャッシュで読み込まれるのでR2までリクエストが届かず、1000万回の無料枠を使い切らずに利用できます。Class Aの一覧取得リクエストのみ、同期系のプラグインを使っていると超える可能性があるため注意です。通常利用で課金されることはなさそうです。
Cloudflare R2のバケット作成とAPIトークンの発行手順
まずは保存先となるCloudflare R2のバケットを用意します。
CloudflareのダッシュボードからR2を選択し、バケットを作成を選択します。

任意のバケット名を設定し、位置情報は自動、デフォルトのストレージクラスはStandardに設定します。

バケットの作成が完了したら、WordPressからR2へ接続するための認証情報を準備します。Cloudflare管理画面で専用のAPIトークンを発行し、アクセスキーなどを取得する手順を解説します。
まず、CloudflareダッシュボードのR2概要ページを開き、画面下部のAPIトークン管理をクリックします。

続いてAPIトークンを作成を選択します。
トークン名:WordPress接続用など分かりやすい名称を入力します
権限:オブジェクトの読み取りと書き込みを選択
対象バケット:特定バケットのみ許可するか全バケットを対象にするかを選択
TTL(有効期限):WordPressとの接続を切断させないため、無制限に設定

設定完了後、APIトークンを作成をクリックすると認証情報が表示されます。
画面に表示される以下の情報をメモに控えてください。
アクセスキーID:S3互換APIで接続するためのIDです
シークレットアクセスキー:認証に必要なパスワード情報です。この画面を閉じると再表示されません
エンドポイントURL:S3 APIリクエストの送信先となるURLです
Google Cloud StorageからCloudflare R2へ既存画像を同期する
続いて現状の画像データをGoogle Cloud StorageからCloudflare R2へコピーします。
Google Cloud StorageでHMACキーを発行する
データ移行を実行するにはGoogle Cloud Storage側でS3互換の認証情報であるHMACキーを発行します。この鍵を使うことでCloudflare R2がGoogle Cloud Storage内の画像データを読み取れるようになります。
Google Cloudコンソールを開き、Cloud Storageの設定画面に移動します。インターオペラビリティタブを選択し、サービスアカウントのHMACキーを作成をクリックします。
画面に表示されるアクセスキーとシークレットを作成して手元に控えます。
Cloudflare R2でデータ移行を開始する
CloudflareのR2管理画面を開き、上部のファイルを移行するを選択します。

Google Cloud Storageを選択します。
ソースバケット名:Google Cloud Storageで使っていたバケット名を入力します
アクセスキーID:先ほど発行したHMACアクセスキーを入力します
シークレットアクセスキー:先ほど発行したHMACシークレットキーを入力します
ターゲットバケット:作成しておいたCloudflare R2のバケットを選択します
入力完了後、移行を開始をクリックすると自動的にデータの転送が始まります。しばらく待ちます。私のブログのメディア数は12000個程度でしたが、3~5分程度で終わりました。
転送完了とフォルダ階層の確認
転送完了の表示が出たら、Cloudflare R2のバケット内を開いてファイル一覧を確認します。私は年、月でフォルダをわけていたので、そのとおりに移行されていることを確認しました。

具体的には、2026/09/ファイル名のような元の階層が保持されていることを確認しました。
WP-Statelessの無効化とメディアオフロードプラグインの導入
データのコピーが終わったら、WordPress側の配信設定を切り替えます。
WP-Statelessを無効化し、Cloudflare R2に対応したプラグインAdvanced Media Offloaderをインストールして有効化します。

プラグインの設定画面を開き、先ほど発行したCloudflare R2のアクセスキーID、シークレットアクセスキー、バケット名、エンドポイントURL、配信用のカスタムドメインを設定します。

その下にあるFile Handlingでは以下のように設定します。Retention PolicyをSmart Local Cleanupにしておくと不要なファイルを削減されるため、容量を多少節約できます。

サーバー上に画像がない場合のメタデータ強制設定
WP-Statelessなどでサーバーローカルから画像を完全に削除して運用していた場合、移行先プラグインがローカルファイルを認識できず、オフロード未完了と判断してしまうことがあります。
この場合、WP-CLIコマンド等を使用してデータベース内のメタデータへ直接移行済みフラグ(advmo_offloaded)を設定します。wpコマンドが入っていない場合はインストールをお願いします。
wp post meta set 2713 advmo_offloaded 1 --allow-root
私の場合は上記に加えて、プラグインによって生成されるURLから年月フォルダ(Y/m/)が抜け落ち、ルート直下に書き換わってしまうトラブルが発生しました。
例:https://media.kuimal.com/2026/09/image-hogehoge.pngが正ですが、URLがhttps://media.kuimal.com/image-hogehoge.pngとなり、表示できていなかったです。
自作プラグインによる年月のURL補正
WordPressのフィルターフックを使用した自作プラグインを作成してURLを補正しました。
WordPressのルートディレクトリ配下にある wp-content ディレクトリ内に mu-plugins ディレクトリを作成し、その中に補正プログラムを配置します。
まずはディレクトリの作成と移動を行います。
cd /var/www/html/wp-content
mkdir -p mu-plugins
cd mu-plugins
次にVimで補正用のPHPファイルを作成して開きます。
vim fix-r2-url.php
Vimが起動したらキーボードの i キーを押して挿入モードへ切り替え、以下のプログラムを貼り付けます。
<?php
/*
Plugin Name: Fix R2 Media Offloader URL
*/
add_filter('wp_get_attachment_url', function($url, $post_id) {
$file = get_post_meta($post_id, '_wp_attached_file', true);
if ($file && strpos($url, 'media.kuimal.com') !== false) {
return 'https://media.kuimal.com/' . ltrim($file, '/');
}
return $url;
}, 999, 2);
貼り付けが完了したら Esc キーを押してコマンドモードに戻り、:wq と入力してEnterキーを押すことで保存して終了します。
このプラグインで、データベースに記録された本来のファイルパス(_wp_attached_file)を参照し、正しく年月が入ったURLで画像を出力できるようになります。この機能は、既存の移行した画像ファイルのURLに対して有効であり、新規にアップロードしたファイルについては、自動的に正しいURLが入ります。
また、この自作プラグインを削除すると再びURLがルート直下に戻って画像が表示されなくなるため、そのままサーバー内に残して運用します。
WordPress内部のキャッシュを消去する
データベースの置換や補正プラグインの設置が完了したら、古いURLの参照がWordPress内に残らないようキャッシュをクリアします。
以下のコマンドを実行してオブジェクトキャッシュを全破棄します。
wp cache flush --allow-root
動作確認とGoogle Cloud Storageの安全な削除
すべての設定が完了したら、動作確認を行います。

WordPressのメディアライブラリからテスト画像を新規アップロードし、正しいURLで表示されるか確認します。また、既存の記事を開き、デベロッパーツールのコンソール画面で画像の404エラーが発生していないかを確認します。サイトのファビコンやヘッダーなども合わせて確認してください。
問題なく画像が表示されることを確認し、使用していたGoogle Cloud Storageのバケットやサービスアカウントキーを削除します。これでGoogle Cloud Storageにかかっていた毎月の費用が削減されました。
まとめ
最近ではCloudflareが無料で便利なサービスを次々と展開しているのでありがたいです。一昔前はSSL証明書の自動更新をcronで実行してましたが、今ではCloudflareがすべて自動化してくれます。
費用を抑えて自分の趣味ブログをかけるのは本当に楽しいです。

