
X(Twitter)シェアで書影が出ない問題を動的 OGP エンドポイントで解決した記録
問題と対応のまとめ 開発を担当しているサービスでは、作品詳細ページにX(旧Twitter)のシェアボタンを設置しています。ボタンを押すと書影(表紙画像)付きのTwitter Cardとしてツイートに表示されることを想定していました。しかしシェアボタンの動作確認中に実際にシェアしてみたところ、書影が表示されずテキストのみのカードになっていることを発見しました。 当初のシェアボタンは作品詳細ページのURLを直接参照しており、書影はページを開いた後にJavaScriptが内部APIを呼び出して動的に取得・表示する実装でした。 原因はこの構成にあります。フロントエンドはNext.jsの静的エクスポートで動いているため、初回のHTMLレスポンスには書影が含まれていません。Twitter Cardはページの静的HTMLに埋め込まれたメタタグを読み取る仕組みのため、CSR(クライアントサイドレンダリング)で取得した書影をOGPタグとして渡せません。 SSR(サーバーサイドレンダリング)への移行も検討しましたが、ホスティング構成の大幅な変更が必要なため見送りました。詳しくは後述の「なぜ専用エンドポイントを追加したか」で説明します。 解決策として、api-frontend にTwitterbot向けのOGP生成エンドポイントを新設しました。フロントエンドのXシェアURLをそこへ向けることで、書影付きのTwitter Cardが表示されるようになりました。この対応は調査・実装・動作確認を含めて約2週間で完了しました。 OGP と Twitter Card について OGP(Open Graph Protocol) とは、Webページの情報をSNSやメッセージアプリが正しく読み取れるよう構造化するための仕様です(ogp.me)。OGPタグを設定していない場合、SNSでシェアしてもURLがそのままテキストとして表示されます。HTMLの <head> に og:title・og:description・og:image などのタグを記述します。これにより、URLシェア時にタイトル・説明・画像のプレビューが自動生成されます。Facebookが策定し、現在は多くのSNS・チャットツールで対応しています。 Twitter Card はX(旧Twitter)が提供するURLシェア時のリッチリンクプレビュー機能です。OGPタグをベースに twitter:card・twitter:title・twitter:image などの専用タグを加えることで、ツイート内に画像・タイトル・説明文を含むカードが生成されます。 サービスの構成 このサービスのフロントエンドは2つのリポジトリに分かれています。ユーザー向け画面を担当する frontend と、決済・外部連携など動的なサーバー処理が必要なAPIを担当する api-frontend です。 本記事の実装は api-frontend の以下の環境で行いました。 技術 バージョン Next.js 13.4.12 TypeScript 5.1.6 graphql-request 6.1.0 Node.js 20.x frontend はNext.jsの静的エクスポートでHTML・CSS・JSをCDNで配信しています。書影の画像ファイルも別のCDN上に置かれており、Next.jsのコードには含まれていません。 api-frontend は決済・外部連携を担うAPIルートをホスティングしているNext.jsアプリです。サーバーサイドで動作するため、リクエスト時に動的な処理が可能です。 OGPエンドポイントはリクエスト時に内部APIから書影データを動的に取得する必要があります。静的エクスポートの frontend はビルド済みファイルを配信するだけでリクエスト時にコードを実行できないため、api-frontend に実装します。 対応前後のリクエスト経路の違いを示します。 【対応前:通常ユーザー・Twitterbot 共通】 シェア URL ─→ frontend(CDN)─→ HTML 配信(OGP タグに書影なし) ↑ Twitterbot はここで OGP を読む → 書影なし 【対応後:User-Agent で処理を分岐】 シェア URL ─→ api-frontend /api/ogp/titles/{slug} ├─ 一般 UA ────→ 302 ─→ /titles/{slug}/detail/(frontend) └─ Twitterbot ─→ 内部 GraphQL API で書影データ取得 ─→ CDN URL + origin_key で og:image 組み立て ─→ OGP タグ入り HTML を 200 で返す 静的エクスポートと Twitterbot の相性問題 静的エクスポートとは Next.jsには主に2つの出力方式があります。 ...