#OGP#Cloudflare#Next.js#R2#アーキテクチャ

作品OGPを事前生成中心へ移行した理由と設計

orimemo開発チーム
5 min read

orimemoでは、作品詳細ページのSNS共有カードをより安定して表示するために、 作品OGPの仕組みを 都度生成中心 から 事前生成中心の静的配信 に移行しました。

この記事では、

  • 技術面でなぜその移行が合理的だったのか
  • 仕様面でどこが重要だったのか
  • どんな利点があるのか
  • どの程度ほかのページやサービスにも転用しやすいのか

を、現在の実装に沿って整理します。

背景

作品ページのOGPは、単なるタイトル画像ではなく、 以下のような情報を含む「作品カード」として見せたい要件がありました。

  • 作品画像
  • タイトル
  • カテゴリ
  • 作者
  • デザイナー
  • 難易度
  • 折った日

この要件自体は自然です。 SNS上で共有された時点で、作品の内容がひと目で伝わるからです。

一方で、OGP画像は通常の画面表示とは違い、 人間ではなく外部クローラが取得する画像です。 そのため、通常のアプリ画面よりも、以下を優先する必要があります。

  • 常に公開URLだけで取得できること
  • 短く安定したURLであること
  • 失敗時にタイムアウトしにくいこと
  • 画像形式とレスポンスヘッダが安定していること

以前の方式と、その合理性

最初の方式は、ページ側で作品情報をクエリに展開し、 /api/og/works?... に渡してその場でPNGを生成するものでした。

この方式にも合理性はありました。

1. DBアクセスを画像エンドポイントから切り離しやすい

ページ側で必要な情報を整形して渡してしまえば、 OGPエンドポイントは「与えられた情報を描画するだけ」で済みます。

  • 依存関係が少ない
  • 認証を避けやすい
  • デバッグしやすい

という利点がありました。

2. 実装速度が速い

next/ogImageResponse で描画し、 作品画像を埋め込めば、比較的短い実装でリッチなOGPを作れます。

3. 仕様的にも一応は成立する

OGPの仕様上は、og:imagetwitter:image が 有効な画像URLを返せばよく、クエリ付きURL自体は問題ありません。

つまり、初期段階では十分に合理的な構成でした。

どこが限界だったのか

問題は、クローラが本番環境でその場生成を叩く ことでした。

とくにCloudflare Workers上では、

  • 元画像の取得
  • webp の正規化
  • フォント読み込み
  • JSXレイアウトの描画
  • PNG生成

を1リクエスト内で完結させる必要があります。

これが重なると、作品によっては Worker の CPU 制限に達し、 Error 1102exceededCpu が発生しました。

ここで重要なのは、 「アクセスが多いから落ちた」のではなく、1回の生成コストが高すぎた」 という点です。

つまり、都度生成を速くする工夫だけでは限界があり、 アーキテクチャそのものを見直す必要がありました。

現在のアーキテクチャ

現在は、以下の構成にしています。

  1. 作品ページは twitter:image / og:image に短いURLを出す
  2. URLは https://orimemo.com/og/works/{workId}?version=...&rev=...
  3. そのURLはまずR2上の生成済みPNGを探す
  4. あればそのまま返す
  5. なければ一度だけ生成してR2へ保存する
  6. 保存・更新時は waitUntil で背景生成を走らせる
  7. 既存の公開作品はバックフィルで先回り生成する

実態としては、完全なオンデマンド生成でも、 完全なビルド時固定画像でもありません。

orimemoの現在地は、 事前生成中心 + 未生成時の一回生成 + 保存後の背景温め というハイブリッドです。

この設計が技術的に合理的な理由

1. 高コスト処理をクローラのホットパスから外せる

SNSクローラが画像を取りに来た瞬間に、 重い変換と描画を毎回やるのが危険でした。

現在は、生成済みPNGがあれば、 クローラはR2に保存された静的画像を読むだけです。

これにより、CPU制限に達しにくくなります。

2. 画像形式を常に image/png に固定できる

元画像が jpg でも webp でも、 最終的に返すOGPはPNGです。

この統一はかなり重要です。 クローラごとのデコーダ差異や、可変な変換経路を減らせます。

3. URLを短く、意味的に安定させられる

現在のURLは workId ベースです。 長いクエリに作品情報を全展開しないため、

  • URLが短い
  • 共有しやすい
  • クローラ視点でも扱いやすい
  • 「この画像はこの作品のもの」と意味が明確

という状態になります。

4. 更新の無効化戦略を組み込みやすい

現在は versionrev をURLに含めています。 さらにR2の保存キーにも、

  • workId
  • 作品バージョン
  • 更新時刻
  • レンダラの版番号

を反映しています。

これにより、画像を差し替えたくなったときに 「古いキャッシュを壊しつつ、新しい画像だけを出す」がやりやすくなります。

5. 失敗点を分離できる

都度生成だと、クローラの1回のアクセスにすべての失敗が乗ります。

現在は少なくとも論点を分けられます。

  • 作品データの取得
  • OGP描画
  • PNG保存
  • 配信

どこが壊れたかを見分けやすくなり、運用もかなり楽になります。

仕様面での合理性

OGPまわりは、見た目だけでなく「クローラにどう見えるか」が本質です。 その意味で、今回の構成は仕様面でも合理的です。

公開URLだけで完結している

クローラは通常、ログインもCookieも持ちません。 そのため、twitter:image / og:image は 公開URLだけで完結している必要があります。

今回の /og/works/{id} はそこを満たしています。

robots.txt と競合しにくい

/api/ 配下の画像URLは、運用によっては robots.txt とぶつかりやすくなります。

OGP専用の /og/ 配下に出したことで、 「共有用の公開画像」であることがURL構造としても明確になりました。

クローラ依存の差に強い

X、Slack、Discord、LINE、メッセージアプリ系のクローラは、 それぞれ細かな癖があります。

ただし共通しているのは、

  • 短く安定した絶対URL
  • 公開アクセス可能
  • 標準的な画像形式
  • タイムアウトしにくいレスポンス

を好むことです。

今回の構成は、その共通条件に寄せています。

利点

この構成の利点をまとめると、以下です。

安定性が高い

  • CPU超過しにくい
  • クローラが失敗しにくい
  • 本番だけ壊れる、が起きにくい

運用しやすい

  • バックフィルできる
  • 作品更新後に背景で温められる
  • 画像キーが決定的で追跡しやすい

キャッシュに強い

  • R2とCDNに乗せやすい
  • immutable戦略が取りやすい
  • 再生成タイミングを制御しやすい

実装責務が明確

  • metadata生成
  • OGPデータ整形
  • レンダリング
  • 保存
  • 配信

が分離されるので、保守しやすくなります。

汎用性

この設計は、折り紙作品OGPだけの特殊解ではありません。

次のようなケースにもそのまま応用できます。

  • 記事ごとのSNSカード
  • ユーザープロフィールの共有カード
  • ECの商品カード
  • イベントページの告知画像
  • ダッシュボードのスナップショット共有

共通パターンは同じです。

  1. 表示用データを純関数で整形する
  2. 描画は専用のレンダラに閉じ込める
  3. 配信用URLは短く固定する
  4. 生成結果をオブジェクトストレージへ保存する
  5. 更新時にだけ再生成する

特に、serverless / edge 環境で重い画像合成を扱うなら、この構成はかなり汎用的です。

なぜ「完全静的」ではなくハイブリッドなのか

全作品を完全に事前生成しておく設計もありえます。 ただ、orimemoでは以下の事情があります。

  • 作品が後から追加される
  • 公開/非公開が切り替わる
  • バージョンが増える
  • 描画ロジックの更新で再生成したい

このため、 未生成なら一度だけ作る という逃げ道を残しておく方が実運用に向いています。

同時に、保存時の背景生成とバックフィルを組み合わせることで、 実際のクローラ到達時には大半が「生成済み」である状態を目指せます。

このバランスが、いまのorimemoでは最も実務的でした。

まとめ

作品OGPを事前生成中心へ移行した理由は単純で、 共有カードは「描けること」より「確実に返せること」の方が重要だからです。

今回の設計で得られたポイントは次の通りです。

  • その場生成は初期実装としては合理的
  • ただし本番クローラの取得経路では重すぎることがある
  • OGPは短い公開URLと静的配信に寄せるほど安定する
  • 事前生成、背景温め、バックフィルの組み合わせが運用に強い
  • この構成はほかのSNSカードにも流用しやすい

OGPは見た目の装飾ではなく、配信設計そのものです。 とくにCloudflare Workersのような制約のある環境では、 「画像をどう描くか」よりも、 どのタイミングで描き、どこに保存し、どのURLで返すか まで含めて設計する方が、最終的には強い実装になります。

o

orimemo開発チーム

orimemoの技術的な側面を深掘りし、実装の舞台裏をお届けしています。 フィードバックや質問があれば、GitHubでお気軽にお声がけください。

関連記事