30代エンジニアの備忘録のカバー画像

30代エンジニアの備忘録

組み込み開発・信号処理・プログラミングの技術メモと短編小説

Cloudflare R2でブログ画像を管理する|Notionブログの画像消滅問題を解決する方法

2026年5月時点、Notion をヘッドレス CMS にしたブログでは、Notion API 経由の画像 URL に有効期限があり、公開からしばらく経つと表示が切れることがあります。R2 に画像を置くと読者側では欠落しにくく、差し替え・削除をオブジェクトキーで管理しやすくなります。本記事では Cloudflare R2 を安定配信先に使った手順と、リポジトリ側の運用メモをまとめます。

Notion 画像の有効期限切れは何が起きる?

Notion APIで取得した公開用の画像URLは、環境にもよりますが短期間で失効することがあります。SSG でビルドした直後は表示されていても、時間が経つと URL が無効になり、記事本文の画像が壊れたアイコンに見える、という問題は Notion をソースにしたサイトでありがちです。

Cloudflare R2 で画像をホスティングする方法は?

アーキテクチャ概要
アーキテクチャ概要
なぜ Cloudflare R2 を選ぶ?
  • コスト:ストレージと操作には無料枠があり、小規模サイトではその範囲に収めやすいことが多い。条件は変更され得るので、運用時点の公式料金表を確認
  • S3 と違う料金モデル:データの出入りや CDN との組み合わせで体感が変わる。自分のサイト構成での出方だけ押さえておくと安心
  • S3互換API:既存のaws-sdk等がそのまま使え、移行コストが低い
  • カスタムドメイン対応:images.example.comのようなURLでアクセスでき、SSL証明書も自動発行

R2 バケット設定は何から始める?

1. R2バケットの作成

Cloudflare ダッシュボードの「R2 Object Storage」から「Create bucket」を選び、バケット名(例:sss-blog-images)を指定して作成します。リージョンは Automatic で問題ありません。詳細は R2 のドキュメント を参照してください。

2. カスタムドメインの接続

バケット設定の「Custom Domains」からサブドメイン(例:images.sssstudy.com)を接続します。CloudflareでDNSを管理している場合、DNS設定とSSL証明書の発行は自動です。これにより、https://images.sssstudy.com/ファイル名 の形式で画像にアクセスできます。

3. APIトークンの発行

プログラムからのアップロード用に、「R2 Token」ページで「Object Read & Write」権限のAPIトークンを作成します。

# .env(環境変数ファイル)
R2_ACCOUNT_ID=your_account_id
R2_ACCESS_KEY_ID=your_access_key
R2_SECRET_ACCESS_KEY=your_secret_key
R2_BUCKET_NAME=sss-blog-images
R2_PUBLIC_URL=https://images.sssstudy.com
4. アップロードスクリプトの統合

npm run r2:upload でアップロードから URL 取得まで自動化しています。@aws-sdk/client-s3 で S3 互換エンドポイントに接続する構成です(R2 と S3 API)。

# 画像のアップロード例
npm run r2:upload -- ./image.png thumbnails/my-article.png
# → https://images.sssstudy.com/thumbnails/my-article.png

# ディレクトリ構成(推奨)
# thumbnails/    ← 記事サムネイル
# articles/slug/ ← 記事本文内の画像
# assets/        ← ロゴ等の共通アセット
💡
Notion 経由の公開用画像 URL は、本文で書いたように短期間で失効することがあります。R2 に置いたオブジェクトへは、このサイトでは安定した公開 URL を当てているため、過去記事で画像リンクが途中で死ににくくなります。サムネイルは Notion の FeaturedImage にも同じ公開 URL を入れておくと管理が楽です。

導入後の運用で何を押さえる?

  • 差し替え:同じキーで上書きアップロードするか、新キーを発行して Notion 本文の URL を差し替えたうえで、不要な旧オブジェクトを r2:delete などで削除する
  • ビルド:Notion を更新したあとは SSG の再ビルドが必要(静的 HTML に焼き込む前提)
  • 無料枠の見方:ストレージ・クラス A/B 操作の単価は更新され得るので、Cloudflare R2 の料金 を転送量・操作回数の想定とあわせてざっくり当てはめます。

公開前チェックリストは?

  1. バケットとカスタムドメインが期待どおり HTTPS で開ける
  2. 公開読み取り用ポリシー/Workers 経由など、自分が選んだ公開経路で GET できる
  3. アップロード用トークンがリポジトリにコミットされていない(.env はローカル/CI のみ)
  4. 記事ごとに articles/<slug>/ のようにキーを分け、サムネは thumbnails/ にまとめる
  5. 画像を差し替えたら、旧 URL を他記事が参照していないか grep 相当で確認してから旧オブジェクト削除

※ 環境変数名(例: R2_SECRET_ACCESS_KEY)をそのまま本文 HTML に書くと、サイトによってはマスキングやリンク崩れの原因になることがあります。デプロイ後に実ページで表示を確認してください。

この手順は「Astro × Notion ブログ構築」シリーズ([series-astro-notion](/posts/series-astro-notion/))の画像・デプロイを固めるパートです。CMS とホスティングの前提が固まったあとで読むと全体図に載せやすいです。