作品詳細の動的OGP生成で詰まったポイントと解決策
作品詳細ページのOGPを「情報量の多い動的画像」にしたい、という要件から実装を進めたところ、 Next.js + OpenNext/Cloudflareの組み合わせでいくつか落とし穴がありました。 本記事では 何が起きたか / どう直したか / なぜその設計にしたか / そもそも標準的にはどうあるべきか を整理します。
背景
- 作品詳細ページのOGPを、タイトルだけでなく作者・紙・難易度などの作品情報を見せる画像にしたい
- 作品データはDBから取得可能だが、クローラが確実に取得できるOGPにしたい
- 公開/非公開や権限の影響を受けない安全なOGP生成が必要
そこで、ページ側でOGP表示用パラメータを先に作り、/api/og/works にクエリとして渡して画像を生成する方式にしました。
起きた技術的問題
1. Edge Runtimeで ImageResponse が「コンストラクタではない」エラー
ローカルで runtime = 'edge' のままOGP APIを叩くと、以下のエラーが発生しました。
TypeError: OGImageResponse is not a constructor
OpenNext/Cloudflare環境では next/og のEdge実装がスタブになっており、
ImageResponse が実体を持たず、関数として解決されてしまうために起きる症状でした。
2. Node RuntimeでもWebAssemblyが壊れていた
runtime = 'nodejs' に切り替えても以下のエラーが出ました。
WebAssembly.instantiate(): expected 4 bytes, fell off end @+0
調査すると node_modules/next/dist/compiled/@vercel/og 配下の
resvg.wasm / yoga.wasm が 1 byte になっており、壊れていました。
(依存の取得失敗・破損が原因)
どう解決したか
1. Node RuntimeでOGP生成
OpenNext/Cloudflare環境でEdgeがスタブになるため、runtime = 'nodejs' に切り替えました。
(Vercel環境ならEdgeで動くのが標準ですが、OpenNextでは要調整)
2. wasmが壊れている場合はフォールバック
resvg.wasm / yoga.wasm のサイズをチェックし、壊れていたら静的OGPへリダイレクト。
ImageResponse のimportが失敗しても落ちないようにガードしました。
どうよくなったのか
- 作品詳細のOGP画像がタイトル・作者・用紙・難易度・タグなどを含むリッチなものに
- DB不要のクエリベースOGP生成で、クローラが確実に取得可能
- パラメータが欠けても レイアウト崩れしないフォールバック
- 壊れた環境でも 静的OGPに自動退避して落ちない
設計意図(なぜこの方式にしたか)
OGP生成は「外部クローラが確実に叩ける」ことが最優先
OGPはSNSクローラが取得するため、
- Cookie/認証が前提になるDBアクセスを避ける
- 速度・安定性優先でクエリパラメータだけで生成
- 欠損があっても壊れない
という方針にしました。
作品詳細ページで先にOGP用データを整理
generateMetadata 内でDBから取得した情報を整理し、
OGPエンドポイントに 必要十分なパラメータ を渡す設計にしています。
標準的にはどうあるべきか(この技術スタックの場合)
Next.js App Router + OpenNext/Cloudflare という前提での「標準」に近い指針は以下です。
- OGP生成は
next/ogのImageResponseを使うのが王道- ただし Cloudflare/OpenNext では Edge 実装がスタブになる場合があるため注意
- Edgeが使えない場合はNode Runtimeに切り替える
- その場合は wasmの配布状態を必ずチェック
- OGP URLは絶対URLにする
metadataBase/getSiteUrl()を活用
- OGPは認証に依存しない設計
- クローラに確実に返すために、事前生成 or クエリ渡しが安全
- フォールバックを用意する
- 生成失敗時に静的OGPへ落ちる設計が安定運用の鍵
まとめ
- Next.js + OpenNext/Cloudflareでは
ImageResponseのEdge実装が使えないケースがある - Node Runtimeに切り替えても wasm が壊れていると落ちる
- そのため 実行環境の差を考慮したフォールバック設計が必須
- OGPは「クローラが確実に取得できる」ことを最優先にしたほうが安定する
orimemo開発チーム
orimemoの技術的な側面を深掘りし、実装の舞台裏をお届けしています。 フィードバックや質問があれば、GitHubでお気軽にお声がけください。