
ヘッドレスCMSのメディアを公開/非公開にする ― R2はバケット単位公開だった
平原 典彦 — Tesseraを開発しているエンジニア。ヘッドレスCMSとAI連携に関心があります。
Tesseraのメディアに「公開/非公開」を足しました。ブログの画像は公開でいいけれど、社内資料や下書きは公開したくない——というニーズに応えるためです。実装で一番の学びは、Cloudflare R2の公開モデルがS3と違うことでした。
S3の感覚:オブジェクト単位のACL
AWS S3なら、オブジェクトごとに private / public-read のACLを設定できます。だから最初は「アップロード時に非公開フラグを立てればいい」と考えていました。
R2の現実:公開はバケット単位
ところがR2は、オブジェクト単位の公開ACLを持ちません。R2で「公開」かどうかは、バケットに公開ドメイン(カスタムドメインやr2.devの公開URL)が付いているかで決まります。付いていればバケット内の全オブジェクトが公開、付いていなければ全部非公開。オールオアナッシングです。
つまり、公開ドメインを付けたバケットに置いたオブジェクトは、ACL: private を指定しても——R2はそれを公開制御に使わないので——キーを知っていれば誰でも取得できてしまう。フラグだけの「ニセ非公開」になってしまいます。
解決:2バケット+署名付きURL
そこで、こう分けました。
- 公開バケット:公開ドメイン付き。ブログ画像などはこれまで通りR2への直リンクで配信
- 非公開バケット:公開ドメインを付けない。ここに置いたオブジェクトはインターネットから直接は叩けず、S3の署名付きURL(短命)でのみ配信する
非公開ファイルを見るときは、認証済みの管理者に対してその都度、有効期限つき(数分)の署名付きURLを発行します。URLが拡散しても、期限が切れれば無効。実体は公開ドメインが配信できない場所にあるので、「本当に非公開」になります。
公開/非公開の切り替えは、バケット間でオブジェクトを copy+delete して移動し、DBの visibility を更新します。
落とし穴のまとめ
- R2はオブジェクト単位の公開ACLを持たない。公開はバケット単位
- 「非公開」は、公開ドメインを付けないバケット+署名付きURLで実現する
- 公開ドメインを付けたバケットに置いた時点で、フラグに関係なく公開扱い
S3の常識をそのまま持ち込むとハマるポイントでした。同じくR2で公開/非公開を分けたい人の参考になれば。