配信物と退避物を分け、公開バケットに状態ファイルを置かない構成へ直した
今回やったこと / この記事について
公開用ストレージと、収集・運用状態を退避する領域の境界を見直した。対象は記事画像をR2へ保存する構成で、状態用の領域から画像を配信していた矛盾を解消し、配信物を媒体別の接頭辞へ分けた。ここで扱うのは、画像を置く場所の整理ではなく、「公開用領域に置いたものは外部から配信される」という構成上の前提をコードと設計定義に一致させる作業である。
宣言と実態がずれていた
ポリシー検証スクリプトは、追跡中スレッドの状態を置く管理用接頭辞を非公開として扱っていた。一方で、公開済み記事の画像約1,000枚が同じ接頭辞から公開ドメインで配信されていた。画像が見えること自体は配信として動いていても、棚の宣言は「誰でも落とせない場所」だったため、構成図と実際の出口が食い違っていた。
このとき非公開宣言を緩める方法もある。しかし、それでは状態ファイルまで公開バケットに素で置ける余地を残す。採用したのは宣言を守り、配信物の側を移す方法だった。管理用接頭辞には稼働中の状態だけを残し、公開画像は配信物専用の接頭辞へ寄せる。
旧: state/<識別子>/img_NNN.webp
新: assets/img/<媒体>/<識別子>/img_NNN.webp
媒体単位の棚を持つ理由
配信ルートを一つにまとめない理由は、ファイルを探しやすくするためだけではない。異なる媒体の記事画像が同じ接頭辞から配信されると、URLを見るだけで同じ基盤を共有していることが分かる。画像の内容に禁止語が無くても、URLの構造が運用上の関係を露出する可能性があるため、assets/img/<媒体A>/とassets/img/<媒体B>/を分けた。
実装は画像キー生成処理へ集約した。媒体・識別子・連番からオブジェクトキーを作り、未知または未確定の媒体を未割り当て領域へ落とす。未確定の物をどちらかの媒体名で保存すると、後から判定が変わった際に誤った領域から配信するため、未割り当て領域は記事から参照しない一時置き場である。
媒体の最終決定が公開直前になる場合にも備えた。公開用の移送処理は、正しい接頭辞へコピーしてから記事内のURLを更新する。移送元を消さないのは、移送処理が失敗した際の再試行やログ追跡を可能にするためだ。移せなかったURLは無理に書き換えず、公開前の走査検査が公開を止める。パス文字列を機械的に生成しただけで完了とみなさず、公開直前に実在性と正しい接頭辞であることを検証する設計にした。
既存画像を動かさない判断
新しい規則を入れたからといって、既存の約1,000枚を一括移送はしなかった。公開済み記事のURLをリダイレクト無しで変更すると、過去投稿のOGPや保存済みリンクが壊れるためである。旧接頭辞に残る画像は、直し忘れではなく、過去の公開互換性を守るための意図的な残置と台帳へ記録した。新しく配信する画像だけを新しい棚へ載せる段階移行にしている。
| 種類 | 接頭辞 | 扱い |
|---|---|---|
| 収集状態 | state/ | 非公開。記事画像は置かない |
| 配信画像 | assets/img/<媒体>/ | 公開配信物 |
| 未確定画像 | assets/img/_unassigned/ | 振り分け前。記事から参照しない |
残る制約もある。R2_PUBLIC_DOMAINが全媒体で共通なら、接頭辞を分けてもホスト名の共有までは隠れない。そこまで分離するには媒体ごとのカスタムドメインが必要で、今回の変更範囲には含めていない。解決した範囲と未解決の範囲を同じ台帳に残すことが、ストレージの安全性を過大評価しないために重要になる。
検査を合流点に置く
画像保存の呼び出し元は複数あるため、個々のスクリプトだけを目視しても抜ける。アーカイブの合流点でキー安全性の検査を通し、公開前には記事の走査検査を通す。回帰テストには、正しいキーを通す対照群と、非公開領域や他媒体のキーを止める検査を置いた。実装が存在することではなく、実際に呼ばれ、壊れた入力で止まることを見るのが要点である。
やってみてわかったのは、ストレージの公開範囲はバケット名や説明文ではなく、最終的な公開URLとオブジェクトキー(配置パス)の設計で決まるということだ。退避物と配信物を同じ棚に置かず、媒体の境界も鍵の生成時点から持たせる。既存の互換性を守りながら、新規分の出口を機械的に狭めるのが現実的な移行になる。
試した環境・参照
- R2の公開配信バケットと非公開退避バケットを使う構成
- 配信鍵と媒体別棚の導入
- R2の公開範囲判定、画像キー生成、媒体別接頭辞の回帰テスト、ストレージ設計定義
更新履歴
- 2026-09-17: 初稿。配信棚と非公開棚の分離を整理。
訂正履歴
なし。