Cloudflare R2 画像URL表示の問題:詳細な解説と修正方法

問題の説明

フォーラムでの画像表示に問題があることが最近確認されました。具体的には以下の通りです。

  • 画像のサムネイルが正しく表示されない。
  • 画像をクリックすると、フルサイズの画像は正しく表示される。
  • ブラウザの開発者ツールで、画像URLが正しくないことが示される。

調査の結果、根本原因は画像URLのドメイン名が間違っていることでした。

  • 正しいURL: https://store.starorigin.cc/optimized/1X/[imageID].jpeg
  • 間違ったURL: https://info.7a4081a2d83d3f43fe6b1be1c926fd1c.r2.cloudflarestorage.com/optimized/1X/[imageID].jpeg

システムは、設定されたCDNドメインではなく、生のR2バケットの誤ったドメインを使用しています。

技術的分析

これは、DiscourseがCloudflare R2に保存された画像を処理する際に発生する既知の問題です。場合によっては、s3_cdn_urlが設定されていても、Discourseは最適化された画像(サムネイルなど)を生成する際に、CDN URLではなく生のストレージURLを使用することがあります。

これは以下の要因に関連している可能性があります。

  • Discourseのバージョン
  • S3互換ストレージの設定
  • OptimizedImageテーブルでのURLの保存方法

ソリューション

最もシンプルで効果的な解決策は、クライアントサイドでの修正のためにDiscourseテーマコンポーネントを使用することです。これにはデータベース操作やサーバー設定の変更は必要ありません。ブラウザで間違ったURLを正しいURLに自動的に置き換える短いJavaScriptコードスニペットを追加するだけです。

テーマコンポーネントコード

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer("0.11.1", (api) => {
  // 既に読み込まれた画像を修正
  function fixImageUrls() {
    const badDomain = "info.7a4081a2d83d3f43fe6b1be1c926fd1c.r2.cloudflarestorage.com";
    const goodDomain = "store.starorigin.cc";

    // 通常の画像を修正
    document.querySelectorAll(`img[src*="${badDomain}"]`).forEach(img => {
      img.src = img.src.replace(badDomain, goodDomain);
    });

    // 遅延読み込み画像を修正
    document.querySelectorAll(`img[data-src*="${badDomain}"]`).forEach(img => {
      img.setAttribute('data-src', img.getAttribute('data-src').replace(badDomain, goodDomain));
    });

    // 背景画像を修正
    document.querySelectorAll('[style*="background"]').forEach(el => {
      if (el.style.backgroundImage && el.style.backgroundImage.includes(badDomain)) {
        el.style.backgroundImage = el.style.backgroundImage.replace(badDomain, goodDomain);
      }
    });

    // その他様々な属性を修正
    ['srcset', 'data-large-src', 'data-small-src', 'data-download-href'].forEach(attr => {
      document.querySelectorAll(`[${attr}*="${badDomain}"]`).forEach(el => {
        el.setAttribute(attr, el.getAttribute(attr).replace(badDomain, goodDomain));
      });
    });
  }

  // エディタ内の画像を修正
  api.decorateCooked($elem => {
    fixImageUrls();
  }, { id: 'fix-r2-image-urls' });

  // 初期読み込み後に修正
  api.onPageChange(() => {
    fixImageUrls();
  });

  // 動的に読み込まれるコンテンツを処理
  const observer = new MutationObserver(mutations => {
    fixImageUrls();
  });

  // DOM読み込み後に監視を開始
  if (document.readyState === "loading") {
    document.addEventListener('DOMContentLoaded', () => {
      fixImageUrls();
      startObserver();
    });
  } else {
    fixImageUrls();
    startObserver();
  }

  function startObserver() {
    observer.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['src', 'data-src', 'srcset', 'style']
    });
  }
});

コードの仕組み

このコードは以下の処理を行います。

  1. 包括的な検出: 間違ったドメインを含むすべての画像URLを検索します。
  2. 複数の要素の処理: 様々な画像要素と属性(imgタグ、遅延読み込み画像、背景画像など)を処理します。
  3. 動的な監視: MutationObserverを使用してページの変更を監視し、動的に読み込まれるコンテンツも修正されるようにします。
  4. Discourseへの統合: Discourse APIと統合して、様々な特別なシナリオを処理します。

インストール手順

  1. Discourse管理者アカウントにログインします。
  2. Admin > Customize > Theme Components に移動します。
  3. New ボタンをクリックします。
  4. Create new component オプションを選択します。
  5. 「Fix R2 Image URLs」(またはお好みの名前)と名付けます。
  6. 「Javascript」タブに上記のコードを貼り付けます。
  7. Create ボタンをクリックします。
  8. Enable ボタンをクリックし、適用するテーマを選択します(通常は「Default」)。

検証

インストール後:

  1. フォーラムページを更新します。
  2. 画像を含む投稿を表示します。
  3. サムネイルが正しく表示されていることを確認します。
  4. ブラウザの開発者ツールを使用して、画像リクエストがすべてCDNドメインを指していることを確認します。

クライアントサイドソリューションは、特にサーバーへの直接アクセスが制限されている場合に、最もシンプルで、迅速で、リスクの低いアプローチです。

結論

このシンプルなテーマコンポーネントは、サーバーの変更や複雑な設定を必要とせずに、DiscourseとCloudflare R2ストレージを統合する際の画像URLの問題を効果的に解決します。根本原因に対処するのではなくクライアントサイドで問題を修正しますが、実装が容易で、即時の結果が得られ、理想的なソリューションです。

Discourseサイトでも同様の問題が発生している場合は、このソリューションを試してみてください。

「いいね!」 4

この問題はチャットのみに影響しますか?範囲を把握しようとしています。

この非常に役立つレポートをありがとうございます!

「いいね!」 1

はい、このバグはチャットのみに影響します。私のS3サービスプロバイダーはCloudflare R2です。チャットインターフェイスで画像を送信すると、画像はデフォルトのCDNリンク設定を使用できず、画像が読み込めなくなります。

「いいね!」 1

Upload images in chat can't be show normally when use s3 CDN のケースと似ているようで、修正が試みられましたが、後に元に戻されました。