Next.js SEOガイド

Next.jsサイトをクロール可能、インデックス登録可能、検索で評価される状態にする方法を解説します。2つのルーター、SSG・SSR・ISR・Server Components、App RouterのMetadata API、sitemap.ts、robots.ts、next/image、next/link、静かにSEOを壊すミスを扱います。

初回公開:2026年6月26日 · 最終更新:2026年8月13日 · Advanced
言語

Next.jsは、適切に使えばJavaScript SEOの難しい問題を解決します。App RouterのServer ComponentsとSSG/ISRはコンテンツをHTMLに置き、ネイティブMetadata APIはサーバー上でタイトル、canonical、Open Graphを解決します。sitemap.tsとrobots.tsはファイル規約です。フレームワークはインフラを提供しますがタグは書きません。metadataBaseの欠落、LCP画像のpriority忘れ、Client Componentからのmetadata export、404ビューが200を返す問題に注意してください。

TL;DR — Next.jsは、正しく使えばJavaScript SEOの難しい問題をデフォルトで解決します。App RouterはServer Componentsをデフォルトにし、SSG/SSR/ISRをサポートします。いずれもレンダリング済みHTMLを送るため、レンダリングキューの待ち時間がありません。ネイティブのMetadata APImetadata / generateMetadata)でメタデータを設定し、metadataBaseがないとcanonicalとOG画像が相対URLになることを覚えておきましょう。app/sitemap.tsapp/robots.tsを使い、動的ルートはgenerateStaticParamsで事前生成し、LCP画像にはpriorityを設定し、リンクは実際のnext/linkアンカーにします。Pages Routerもnext/headでSEOに対応できます。失敗は予測可能で、多くはNext.jsのせいではなく実装のせいです。

Next.jsの位置づけ

Next.jsはReactフレームワークなので、JavaScript SEOの内容はすべて当てはまります。専用ガイドにする価値があるのは、Next.jsがJavaScript SEOの多くの問題に、サーバーレンダリング、静的生成、メタデータシステム、sitemap/robots規約という主要な解決策を用意しているからです。難しいのはGoogleが読めるかどうかではありません。Googleは長年JavaScriptをレンダリングしています。適切なレンダリング方式を選び、SEOの基本を未設定のままにしないことが重要です。これはヘッドレスCMS SEOの特殊例でもあります。CMSより、フロントエンドのレンダリング判断が結果を左右します。

「Next.jsサイトである」というだけでは、個々のURLがどう配信されるかは分かりません。レンダリングモード、キャッシュ、Server/Client Componentの境界はルート単位(場合によってはセグメント単位)で決まります。1つのプロジェクトに、静的なマーケティングページ、SSRの商品ページ、Client Componentのダッシュボードを混在させられます。1つのルートの挙動を「アプリ全体」に一般化せず、対象URLをテストしてください。

2つのルーター、2種類の仕組み

Next.jsには2つのルーターがあり、SEOの扱いが異なります。

  • Pages Router(旧モデル) — getStaticProps / getServerSidePropsでデータを取得し、next/head<Head>(またはnext-seoパッケージ)でメタデータを設定します。Server Componentsはありません。
  • App Router(v13以降、現在推奨) — React Server Componentsがデフォルトで、ネイティブのMetadata APImetadata export / generateMetadata)を使います。app/sitemap.tsapp/robots.tsのファイル規約、動的ルート用のgenerateStaticParamsがあります。 Evidence for this claim The App Router uses Server Components and supports generateStaticParams plus metadata file conventions. Scope: Current Next.js App Router behavior; route rendering can become dynamic based on APIs used. Confidence: high · Verified: Next.js: Server and Client Components Next.js: generateStaticParams

どちらでも十分に順位を得られます。App Routerは、next/headを調整する必要のない統合的なメタデータシステムと、最初から使えるServer Componentsを提供するため、新規ビルドならこちらを選びます。ただし「App RouterでなければSEOできない」は神話です。Pages Routerのサイトも数多く正常に順位を得ています。

レンダリングモードとSEO上の意味

GoogleのJavaScript処理は、クロール、遅れて行われるレンダリング、インデックス登録という3段階です。Googleは “All pages with a 200 HTTP status code are sent to the rendering queue.” (翻訳) 「HTTPステータスが200のすべてのページはレンダリングキューへ送られます」と説明しています。Next.jsでは、その処理を待つ前にコンテンツがHTMLへ入る方式を選ぶことが重要です。 Evidence for this claim Google crawls, renders, and indexes JavaScript pages, and successful pages can enter the rendering queue. Scope: Google Search processing, not a promise that a URL will be indexed. Confidence: high · Verified: Google: JavaScript SEO basics

  • Static Site Generation(SSG) — ビルド時に事前レンダリングするため、HTMLをすぐ利用でき、レンダリングキューのリスクがありません。毎分変わらないコンテンツに最適です。generateStaticParams()(App Router)/ getStaticPaths()(Pages Router)が事前生成する動的ルートを決めます。
  • Incremental Static Regeneration(ISR) — 一定間隔後に再検証する静的ページです(export const revalidate = 3600)。クローラーはTTFBの低い静的HTMLを受け取り、内容も新しく保てます。ただし、期間が切れた直後の次のリクエスト(Googlebotの場合もあります)にはまだstaleページが返り、更新版はその次のリクエストで配信されます。価格や在庫のような変動の大きいデータにはSSRが安全です。
  • Server-Side Rendering(SSR) — リクエストごとにHTMLをレンダリングします。クローラーは完成したHTMLをすぐ受け取れますが、サーバー遅延が代償なのでTTFBとLCPを監視します。export const dynamic = 'force-dynamic'、またはリクエスト時API(cookies、headers)を使うとルートはSSRになります。
  • React Server Components(App Routerのデフォルト) — サーバーでレンダリングしてHTMLを送り、コンポーネント自身のJavaScriptは送信しません。コンテンツはハイドレーションの空白なく初期レスポンスに入ります。SEOの最良のデフォルトです。インタラクティブ性は'use client'を付けたClient Componentsに置きます。
  • Client-Side Rendering(CSR) — ブラウザで完全にレンダリングします。Googlebotはレンダリングの波の後にインデックス登録できます(中央値は約10秒ですが、90パーセンタイルは数時間に及びます)。Bingbot、AIボット、ソーシャルプレビューボットなどは空ページを受け取ることがあります。App Routerでは'use client'で明示的に選び、Pages Routerでは主要コンテンツをuseEffectで取得するのを避けます。順位を得たいコンテンツには使わないでください。

覚えておきたいことがあります。「Googlebotがレンダリングできる」と「Googlebotにレンダリングさせるべき」は同じではありません。レンダリングは高コストで遅延し、すべてのクローラーに共通するわけでもありません。

Metadata API(App Router)

Metadata APIはServer Component専用です。ページのレンダリング前にサーバーでメタデータを解決するため、初期HTMLに入ります。layout.jsまたはpage.jsからmetadataをexportします。

export const metadata: Metadata = {
  title: 'My Page',
  description: 'Page description',
}

タグが取得データに依存する場合は、generateMetadata()を使います。

export async function generateMetadata({ params }) {
  const post = await getPost(params.slug)
  return { title: post.title, description: post.description }
}

SEOで重要なフィールド:

  • title — 文字列、テンプレート('%s | Brand')、デフォルト値、絶対値の上書きを使えます。ルートレイアウトでテンプレートを一度設定すれば、各ページのタイトルが継承します。
  • descriptionalternates.canonical(App Routerでcanonicalを設定する正しい方法)、openGraph(画像は絶対URLに解決する必要があります)、twitter(LinkedInとSlackのプレビューにも使われます)、robots(index/followと、max-snippetやmax-image-previewなどのgoogleBot固有指示)。
  • metadataBase — canonicalとOG画像のURLを正しく解決するため必須です。これを忘れるのがNext.jsメタデータの最も一般的なバグで、相対URLがcanonicalとOpen Graphタグに漏れ、ソーシャルプレビューを壊しcanonicalシグナルを曖昧にします。

タイトルテンプレートの例:

// app/layout.tsx
export const metadata: Metadata = {
  metadataBase: new URL('https://example.com'),
  title: { template: '%s | Brand Name', default: 'Brand Name' },
}
// app/blog/page.tsx
export const metadata: Metadata = { title: 'My Blog Post' }
// Output: <title>My Blog Post | Brand Name</title>

2つの注意点。 1つ目は、メタデータがレイアウトからページへ浅くマージされることです。子セグメントでopenGraphのようなネストしたオブジェクトを定義すると親全体を置き換えるため、ページレベルのopenGraph: { title: 'Home' }はレイアウトに設定したopenGraph.imagesを静かに落とします。2つ目は、metadataがServer Componentsでしか機能しないことです。'use client'ファイルからexportすると何も起きません。

ストリーミングメタデータ。 動的レンダリングのページでは、generateMetadataが初期HTMLの後からメタデータをストリームできます。GooglebotはJavaScriptを実行して完全なDOMを調べるため、Googleでは機能します。一方Next.jsは「HTML制限ボット」である Bingbot、Twitterbot、Slackbot、facebookexternalhit を検出し、<head>内にブロッキングメタデータを送ります。Next.js文書は “streaming metadata is disabled for bots and crawlers that expect metadata to be in the <head> tag.” (翻訳) 「メタデータが <head> タグ内にあることを期待するボットとクローラーでは、ストリーミングメタデータが無効になります」と説明しています。これは自動で、設定は不要です。事前レンダリングページではビルド時にメタデータが解決されるため、ストリーム自体がありません。この挙動はNext.js 16.2.10時点のバージョン固有です。更新時にはgenerateMetadata文書を再確認してください。配信経路は入口で異なるため、本番への直接リクエスト(curl -IまたはView Source)と、同じルートへのクライアント遷移の両方でメタデータを検証します。

Evidence for this claim Prerendered Next.js pages do not use streaming metadata because metadata is resolved at build time in the documented path. Scope: route output, metadata and deployment Confidence: high · Verified: Metadata and OG images

next/headによるメタデータ(Pages Router)

Pages Routerでは、メタデータはnext/head<Head>に置きます。

import Head from 'next/head'

export default function Page() {
  return (
    <>
      <Head>
        <title>My Page | Brand</title>
        <meta name="description" content="Description" />
        <link rel="canonical" href="https://example.com/my-page" />
      </Head>
      {/* page content */}
    </>
  )
}

タイトルと説明は_app.jsだけでなくページごとに設定し、ページ分割の各バリエーションを含むすべてのページにcanonicalを置きます。next-seoパッケージは、<NextSeo>コンポーネントと構造化データヘルパーでこれを標準化します。App Routerへ移行する主な意味は、next/headnext-seoからネイティブのmetadata exportへ移ることです。

next-seoパッケージは必要ですか? これはNext.js自体の一部ではないサードパーティプラグインで、執筆時点のv7.2.0は積極的に保守され、アーカイブされていません。文書は用途を明確にしています。App Routerの標準metaタグには、READMEは<NextSeo>ではなくNext.js組み込みのgenerateMetadatametadata exportを推奨します。Pages Routerでは<NextSeo>next/headの便利な抽象化として合理的です。App Routerで残る用途はJSON-LDヘルパー(ArticleJsonLdFAQPageJsonLdなど、useAppDir付き)で、手作業の<script type="application/ld+json">より好むチームもあります。新しいApp RouterではまずネイティブMetadata APIを使ってください。パッケージは任意であり、必須ではありません。

サイトマップ

App Routerでは、app/sitemap.tsというファイル規約が/sitemap.xmlを出力します。

import type { MetadataRoute } from 'next'

export default function sitemap(): MetadataRoute.Sitemap {
  return [
    { url: 'https://acme.com', lastModified: new Date(), priority: 1 },
    { url: 'https://acme.com/blog', lastModified: new Date(), priority: 0.8 },
  ]
}

大規模サイトではgenerateSitemaps()が複数ファイルに分割します(Googleの上限はサイトマップ1つあたり50 000 URL)。各ファイルは/.../sitemap/[id].xmlで配信されます。出力は画像サイトマップ、動画サイトマップ、ローカライズされたalternates.languagesにも対応します。Pages Routerではnext-sitemapを使うか、getServerSidePropspages/sitemap.xml.jsを生成します。

robots.txt

app/robots.tsはrobotsファイルをプログラムで生成します。

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [{ userAgent: '*', allow: '/', disallow: '/private/' }],
    sitemap: 'https://acme.com/sitemap.xml',
  }
}

ユーザーエージェントごとのルールと複数のサイトマップに対応しています。Pages Routerでは静的なpublic/robots.txtを使います。どちらのルーターでも絶対に間違えてはいけないルールは、JavaScriptやCSSをdisallowしないことです。Googleはブロックされたファイルからレンダリングできず、JavaScriptフレームワークではページ全体が空になる可能性があります。

next/imageとCore Web Vitals

next/imageはSEOでこのフレームワークを使う強い理由の1つです。画面下部の画像を遅延読み込みし、width/height(またはfill)を要求して空間を確保し、レイアウトシフト / CLSを防ぎます。WebP/AVIFを自動配信し、sizes propから適切なsrcsetを出力します。最も重要なCWV最適化は、ヒーロー画像または画面上部画像のpriority propです。これをpreloadしてLCPを速くします。

<Image src="/hero.jpg" width={1200} height={630} priority alt="Hero" />

LCP画像でpriorityを忘れるのは、Next.jsのCWVで最も一般的なミスです。実際のNext.jsサイトではCWV問題が広く見られます(Salt AgencyのデータはStatsタブを参照)。altは必須で、装飾画像では空にし、内容を伝える画像では説明的にします。

next/linkと内部リンク

next/linkはHTMLに標準の<a href>アンカーを出力するため、Googleは通常どおりたどれます。本番ではクライアントサイド遷移を追加し、ビューポート内リンクをバックグラウンドでprefetchします。SEOのルールは単純です。内部リンクにはnext/linkを使い、実際のアンカーを生成しないonClickハンドラーやJavaScript遷移で置き換えないこと。そうしたリンクはクロールできません。低価値リンクで帯域を節約するならprefetch={false}を使います。

動的ルートとgenerateStaticParams

generateStaticParams()は、ビルド時にどの動的ルートを事前レンダリングするかをNext.jsに伝えます。

// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await getPosts()
  return posts.map((post) => ({ slug: post.slug }))
}

この方法で生成したページは完全な静的HTMLで、SEOに最適です。これがない動的ルートはデフォルトでオンデマンド(SSR)レンダリングになり、問題はありませんがサーバー遅延が再び発生します。定期更新するコンテンツにはrevalidate(ISR)と組み合わせます。重要な動的URLをすべてgenerateStaticParamsに含め、レンダリングキューを待たせないようにしてください。

構造化データ(JSON-LD)

Metadata APIには構造化データ用フィールドがありません。Server Componentの<script>にJSON-LDを注入し、クライアントバンドルのコストなしでサーバーレンダリングHTMLに残します。

const jsonLd = { '@context': 'https://schema.org', '@type': 'Article', /* … */ }
return <script type="application/ld+json"
  dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />

一般的な型はArticle/BlogPosting、BreadcrumbList、Product、FAQPageです。レンダリングを変更したら、Rich Results Testで検証してください。

Next.js SEOでよくあるミス

実際の監査とここまでのパターンから:

  1. canonicalがない、または相対URLmetadataBaseを忘れたことが原因になり、OG画像URLも壊れます。
  2. LCP画像にpriorityがない — 最大のCWV上の欠落です。
  3. 404ビューが200を返す — 組み込みのnotFound()で実際のステータスを返します。ソフト404はNext.jsサイトで頻発します。
  4. Client Componentからmetadataをexportする — 何も起こりません。Server Component専用です。
  5. 主要コンテンツをCSRにする — 重要な内容をuseEffectで取得すると、Google以外のクローラーは空ページを受け取ります。
  6. History APIではなくハッシュ(#)ルーティングを使う — ビューが別々にクロールできません。
  7. generateStaticParamsをexportしない — 動的ルートが事前生成されずオンデマンドになります。
  8. レイアウト継承でopenGraphを上書きする — 子セグメントはマージせず置き換えます。
  9. robots.txtまたはGooglebotのヘッドレスChromeがスクリプトを読み込めないContent Security PolicyでJS/CSSをブロックする — URL Inspectionでテストします。

デプロイ時の注意

Next.jsはVercelによって開発され、そこでホスティングすると静的ページとISRページのエッジCDNや良好なTTFBとの統合が得られますが、必須ではありません。next.config.jsredirects()で恒久リダイレクトを定義し(permanent: trueなら308または301)、クローラーに確実なシグナルを送り、headers()でセキュリティとキャッシュのヘッダーを設定します。ページ単位のrobotsメタデータが扱いにくい場合は、パス単位のnoindexルールにX-Robots-Tagレスポンスヘッダーを使います。

どこで何を確認するか。 キャッシュ状態、ステータスコード、リダイレクト、ストリーミングメタデータは同じテストにすべて現れるわけではありません。あるチェックでは正常に見えても、別のチェックでは壊れていることがあります。

チェック見る場所レンダリング結果と違う理由
キャッシュ/再検証の経過時間直接リクエスト(curl -I)のレスポンスヘッダー期間が切れた直後のリクエストではISRが古いページを返すことがある
直接のHTTPステータスレンダリングUIではなく本番URLへのcurl -InotFound()のない「見つからない」ビューも200を返す
リダイレクトの挙動実際に発火するコンテキスト(Server Actionのredirect()、Route Handler、またはクライアントのonClickステータスコードとレスポンス経路は遷移先だけでなく呼び出しコンテキストで変わる
ストリーミングメタデータ直接リクエストと同じルートへのクライアントサイド遷移通常のクライアントはストリームを受け、HTML制限ボットはブロッキングメタデータを受ける
クライアントサイド遷移アプリ内で遷移してから<head>を再確認初回ロードで正しいルートも、クライアント遷移後にずれることがある

これらはフレームワークが保証するものではありません。Next.jsはredirects()notFound()、再検証、ストリーミングという仕組みを提供しますが、キャッシュキー、無効化、プレビュー状態、デプロイ設定を正しく扱い、本番でテストする責任は残ります。ローカルだけで確認してはいけません。

最後に動的レンダリングについて。ボットには事前レンダリングHTMLを、ユーザーにはJavaScriptを配信する方法です。Googleは “dynamic rendering was a workaround and not a long-term solution.” (翻訳) 「動的レンダリングは回避策であり、長期的な解決策ではありませんでした」として、推奨を終了しました。Next.jsではSSR、SSG、ISR、Server Componentsが本来HTMLにコンテンツを入れるため必要ありません。監査で認識できるようにしておき、それを新しい基盤にはしないでください。

理想的な経路は、App Router + Server Components + ISR + Metadata API(metadataBase付き)+ priority付きnext/imageです。これらを正しく設定すれば、Next.js SEOの大部分は処理されます。

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.