Next.js

一部のサイトでNext.js(App Router)のOGP画像が表示されない問題

2024/08/11

背景

Next.js を利用しているサイトで、特定の App Router だけ OGP 画像が意図した通りに動作しない問題が発生しました。
実装方法は他の App Router と同じように行なっていたので、原因がわからず困っていました。

原因

SSR 時に client でしか利用できないものを参照していたことで、ページが一旦 500 を返していたことが原因でした。

調査内容

動作確認で Twitter に投稿しようとして、表示されないことに気づきました。
まずは OGP チェック系のサイトで確認してみたところ、OGP 画像が取得できるサイトとできないサイトがあることがわかりました。

OGP 画像が判定できるサイト

OGP 画像が判定できないサイト

OGP 画像の判定が正しくできているサイトもあったので、実装が大きく間違ってないことは確認できました。
まずは WebToolbox で OGP 画像が判定できることを目標としました。

Next.js のgenerateMetadataの記事を読んで修正してみたり、
以下のサイトを参考にして、 dynamic = 'force-dynamicrevalidate = 0 を試してみましたが、解決しませんでした。

Facebook は OGP が正しく取得できていたので試していなかったのですが、ふと Facebook のシェアデバッガーに URL を入力してみると、レスポンスコードが 500 であることに気づきました。
Vercel のログを見てみると、以下のエラーが出ていました。

ReferenceError: window is not defined
    at er (/var/task/.next/server/app/gather/[uuid]/page.js:46:3103)
    at nj (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:46251)
    at nM (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:47571)
    at nN (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:64546)
    at nB (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:67538)
    at nD (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:66680)
    at nN (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:64853)
    at nB (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:67538)
    at nM (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:58560)
    at nN (/var/task/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:12:64546) {
  digest: '3752755390'
}

解決方法

問題のコード

useCallback の依存に window オブジェクトを設定していたこと、コンポーネント内で location オブジェクトを参照していたことが原因でした。

"use client";

export const LoginPage: FC = () => {
  const url = location.href;

  const handleClickGoogleCalIcon = useCallback(() => {
    const googleCalURL = generateGoogleCalendarURL();
    window.open(googleCalURL, "_blank");
  }, [generateGoogleCalendarURL, window.open]);

  const handleClickOpen = () => {
    window.open(url, "_blank");
  };

  return (
    <div>
      <button onClick={handleClickGoogleCalIcon}>Googleカレンダーに追加</button>
      <button onClick={handleClickOpen}>外タブで開く</button>
    </div>
  );
};

解決後のコード

"use client";

export const LoginPage: FC = () => {
  const handleClickGoogleCalIcon = useCallback(() => {
    const googleCalURL = generateGoogleCalendarURL();
    window.open(googleCalURL, "_blank");
  }, [generateGoogleCalendarURL]);

  const handleClickOpen = () => {
    const url = location.href;
    window.open(url, "_blank");
  };

  return (
    <div>
      <button onClick={handleClickGoogleCalIcon}>Googleカレンダーに追加</button>
      <button onClick={handleClickOpen}>外タブで開く</button>
    </div>
  );
};

学び

OGP 画像が取得できない = Next.js で meta タグが正しく設定できていない
という狭い視点で考えていたため、原因特定までかなり時間がかかってしまいました。
意図しない挙動に関しては広い視点で考えることが大切だと改めて感じました。

調査自体は辛かったのですが、Next.js の OGP 系を再度調べるきっかけになったり、use client を指定した場合の挙動について学べたので、結果的には良い経験になりました。

Twitter や Threads などの SNS ではページが 500 を返した時点で OGP 画像の取得をやめてしまうような挙動をするので、正しく設定できているのに OGP 画像が出てこない場合は、レスポンスコードも確認してみましょう。

Twitterフォロー待ってます!