🐷

【Next.js】middlewareでリダイレクト処理を行いつつ、動的OGPを設定する際にハマった話

2023/11/24に公開

はじめに

現在エンジニアをしながらサービス開発をして起業した橋田です。

現在同人誌のフリマサイトを開発中です。

https://twitter.com/dall_develop

みなさん、Next.jsを使っていますか?
Next14がリリースされ、日々フレームワークの進化が早い時代になっていると感じます。

App Routerではサーバーサイドとフロントエンドのコードを書けるので、この教会がなくなりつつあるように感じます。

サーバーサイドの知見に詳しくない私ですが、動的OGPの設定に苦しんだのでここに記録を残しておきます。

metadataを設定したにもかかわらず設定が反映されていない

https://zenn.dev/ayaextech_fill/articles/nextjs13-2_dynamicmetadata

https://nextjs.org/docs/app/api-reference/file-conventions/metadata/opengraph-image

こちらの記事に沿ってmetadataを設定しました。

/**
 * OGP生成
 */
export async function generateMetadata({
  params: { id },
}: ListingPageProps): Promise<Metadata> {
  const listing = await findListingById(id);
  return {
    title: listing.productName,
    description: listing.description,
    openGraph: {
      title: listing.productName!,
      description: listing.description!,
      images: {
        url: listing.images[0].imageURL,
        width: 1200,
        height: 630,
      },
    },
  };
}

ここで、localhostのheadを見てみると、正しく設定されています。

しかしながら、vercelでデプロイしたURLをfacebookやtwitterのOGP確認ツールにて確認してみると、ルートに設定したmetadataが表示されていました。

原因はmiddlewareでリダイレクト処理を行っていたからだった

現在同人誌のフリマサイトを開発しているため、R18商品は成人済みであることの確認を必ず行っております。

ソーシャルメディアのbotに対してもこちらのリダイレクト処理が行われており、年齢確認ページにリダイレクトされていることが原因でした。

/**
 * 年齢認証を通過していない場合に年齢認証画面にリダイレクトするミドルウェア
 * @param req NextRequest
 * @returns NextResponse.redirect | void
 */
export async function middleware(req: NextRequest) {
  return composeMiddleware(req, NextResponse.next(), {
    scripts: [ageCheckMiddleware],
  });
}

対処法

とりあえずの対策として、ソーシャルメディアのbotからのリクエストではリダイレクト処理を適応しないようにしました。

方法としては、ユーザーエージェントのチェックを行い、ソーシャルメディアからだった場合はリダイレクトを適応しないようにしました。

import { NextRequest, NextResponse } from "next/server";

export async function middleware(req: NextRequest) {
  const userAgent = req.headers.get("user-agent");

  // ソーシャルメディアボットのユーザーエージェントをチェック
  if (userAgent?.includes("facebookexternalhit") || userAgent?.includes("Twitterbot")) {
    return NextResponse.next();
  }

  // 特定のパスへのリクエストをチェック
  if (req.nextUrl.pathname.startsWith('/opengraph-image.png')) {
    return NextResponse.next();
  }

  // その他のリクエストに対して年齢確認のリダイレクトを適用
  // ここに年齢確認のロジックを追加
}

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico|opengraph-image.png).*)"],
};

これで表示自体はできるようになりました。

課題

これだとソーシャルメディアのbotの名前をソーシャルメディアの数だけ記載する必要があります。
さらに今後新しいソーシャルメディアが登場した際に追えない可能性があります。

ここはどのようにしているのか不明なので、もしご存じの方が居ましたらコメントなどで教えていただけると助かります。

その他のサービスはどうしているのか?

その他R18商品を取り扱っているサービスは存在します。
他のサービスがこの課題についてどう対処しているのか調べてみました。

FANZA

  • twitterでは普通に表示された
  • facebookだとimageは設定されていなかった。

DLsite

  • リンクに飛んだら透かしが入るようになっているが、twitterやfacebookでは普通に画像は表示されてしまう

流石に画像やリンクをこちらに記載するのは控えますが、OGP画像自体がR18指定の場合、表示しないべきなのか、そこはソーシャルサービス側の責務になるのかまでは調べられていませんが、おそらくソーシャルメディア側になるのかなと予想しています。

個人的な見解

DLSiteの仕組みなどは他の開発メンバーに教えてもらったことですが、個人的にはDLSiteの仕組みが一番素晴らしいと思いました。

年齢確認ページに遷移するのではなく、透かしを表示するようにすれば年齢確認に同意したユーザーしか商品を表示できないです。(多分DOMを直接操作すればできると思われるが、法的な解釈として、そこまではブロックする必要ないのかな?)

また、未成年にR18指定の商品を販売してはならないという法律がありますが、これもいくらでも対策できるので、どのような処置が正しいのかきちんと調べる必要があるなと感じました。

クレジットカード決済のみに対応していれば、購入者は成人しているとの証明ができるので、問題ないですが、出品者側の成人かどうかを担保するには別途Sassなどと契約し、身分証を送ってもらう必要があるかもしれません。

追記

未成年でもアダルトサイトの閲覧自体は合法なようです。

https://www8.cao.go.jp/youth/youth-harm/chousa/h25/net-syogaikoku/3_11.html

https://lmedia.jp/2015/05/03/63850/

フリマアプリであれば、事前に親権者の同意を得ないと取引が取り消しされてしまう恐れがあるようです。

https://i-roi.jp/ec/simulation/column/column-a.html#:~:text=商品の買い手が未,ことになっています。

https://paypayfleamarket.yahoo.co.jp/notice/rule/post_553/

paypayフリマも親の同意があれば出品可能なようですね。

ただこの親の同意というのも、アプリでワンクリックで完了するなら親の同意を正しく行ったかどうかの判定ができません。

ここら辺の法的な課題をどのように解決しているのか気になる部分ではあります。

webの進化が凄まじく、法的な解釈が追いついてない状況なのかもしれません。

ここら辺の法律なども詳しい方がいましたら是非とも教えていただけると嬉しいです。

もし参考になった場合はいいね、拡散などいただけるとありがたいです!
お願いいたします。

Discussion