過去3年間に十数個の WordPress サイトを Next.js に移行してきました。スムーズに進んだものもあれば、火曜日の午前2時にキャリアの選択を疑わせるものもありました。この2つの結果の違いは、ほぼ常に計画段階、つまり移行する前に WordPress が実際に何をしていたのかを理解することでした。

このガイドは、最初の移行の前に誰かから受け取りたかったすべてのことです。完全なジャーニーをカバーしています: 移行すべきかどうかの評価、ヘッドレス CMS の選択、コンテンツの移動、テンプレートの再構築、ランキングを失わずに SEO を処理、トラフィック急増に対応できない Vercel への展開。

さっそく始めましょう。

WordPress から Next.js への Vercel 移行ガイド: 2026年版

目次

2026年に WordPress から Next.js に移行する理由

正直に言いましょう。WordPress は 2026年にはまだ約40%のウェブを駆動しています。どこにも行きません。しかし、去る理由はより説得力があります。

パフォーマンスの上限。 積極的なキャッシュプラグイン (WP Rocket、W3 Total Cache) を使用していても、ほとんどの WordPress サイトは Lighthouse パフォーマンススコアで 70~80 の壁に当たります。プラグインの肥大化、レンダーブロッキング PHP、そしてすべてのページロード時のデータベースクエリは、最適化では完全には排除できないオーバーヘッドを生み出します。

セキュリティの攻撃面。 WordPress の 2025年における文書化されたコア脆弱性は 149 個。プラグインも同様です。すべてのプラグインが攻撃ベクトル。すべてのテーマ更新は潜在的なブレーク。WooCommerce を実行している場合、攻撃面は2倍になります。

開発者体験。 チームが React を知っている場合、PHP テンプレートで構築するのは利き手でない手で書くようなものです。Next.js 15 の App Router、Server Components、組み込みキャッシュにより、WordPress が一致できない最新の開発ワークフローが得られます。

規模での コスト。 マネージド WordPress ホスティング (WP Engine、Kinsta) は適切なパフォーマンスで月額 $30~$300 かかります。Vercel の Pro プラン ($20/ユーザー/月、エッジ関数と自動スケーリング付き) はしばしばより少なくコストがかかりながらより良いパフォーマンスを提供します。

ただし、トレンドだからといって移行しないでください。50個の投稿を持つシンプルなブログで、クライアントが週1回 WordPress 管理画面で更新する場合、移行により解決する以上の問題が発生する可能性があります。移行の最良の候補者は以下の通りです:

  • より良いパフォーマンスが必要な 500+ ページのサイト
  • コンポーネントベースの開発を望むチーム
  • プラグインの競合がメンテナンスの悪夢を引き起こしているサイト
  • WooCommerce パフォーマンスの限界に達している e-コマースサイト
  • エッジで A/B テストとパーソナライゼーションが必要なマーケティングサイト

移行前監査: WordPress が実際に何をしているのか

ほとんどの移行がここで失敗します。人々はブログを移行していると思いますが、WordPress は実際には連絡フォーム、リダイレクト、画像最適化、検索、コメント、認証、cron ジョブ、およびプラグイン設定に埋め込まれた15個の他のものを処理しています。

単一行の Next.js コードを書く前に、すべてをドキュメント化してください:

プラグインインベントリ

プラグインリストをエクスポートして、各プラグインを分類します:

wp plugin list --status=active --format=csv > active-plugins.csv

各プラグインについて: これは何をしていて、Next.js エコシステムで何がそれに置き換わりますか?

WordPress プラグイン 機能 Next.js の置き換え
Yoast SEO メタタグ、サイトマップ、スキーマ next-seo + カスタム sitemap.xml ルート
WP Rocket キャッシング、圧縮 Vercel エッジキャッシュ + Next.js 組み込み
Contact Form 7 フォーム処理 React Hook Form + API ルートまたは Formspree
Wordfence セキュリティ 不要 (管理画面がない)
WPML 多言語対応 next-intl または組み込み i18n ルーティング
WooCommerce e-コマース Shopify Storefront API または Saleor
Advanced Custom Fields カスタムコンテンツモデル ヘッドレス CMS のコンテンツモデリング
Redirection URL リダイレクト next.config.js リダイレクトまたは Vercel 設定
WP Cron スケジュール済みタスク Vercel Cron Jobs または別のサービス
Imagify 画像最適化 next/image と Vercel 画像最適化

コンテンツインベントリ

コンテンツを数えて分類します:

SELECT post_type, post_status, COUNT(*) 
FROM wp_posts 
GROUP BY post_type, post_status;

忘れないこと: カスタム投稿タイプ、タクソノミー用語、ユーザープロフィール、メニュー構造、ウィジェット設定、およびオプション値。その「シンプルな」WordPress サイトはあなたが考えるより多くのコンテンツタイプを持つ可能性があります。

URL マッピング

すべての URL をエクスポート。すべて。Screaming Frog を使用するか、シンプルなサイトマップクロール:

curl -s https://yoursite.com/sitemap_index.xml | \
grep -oP '<loc>\K[^<]+' | \
xargs -I {} curl -s {} | \
grep -oP '<loc>\K[^<]+' > all-urls.txt

このファイルは金です。リダイレクトマッピング、SEO 保存、および移行後の QA テストに使用します。

WordPress から Next.js への Vercel 移行ガイド - アーキテクチャ

ヘッドレス CMS バックエンドの選択

コンテンツを保存および管理する場所が必要です。2026年の3つの最も一般的なパス:

オプション1: ヘッドレス CMS としての WordPress

はい、WordPress をバックエンドとして保持し、フロントエンドとして Next.js を使用できます。WPGraphQL (現在 v2.1) はこれを驚くほど実行可能にします。エディターは親しみのある管理インターフェースを保持します。モダンなフロントエンドが得られます。

// lib/wordpress.js
const API_URL = process.env.WORDPRESS_GRAPHQL_URL;

export async function getPostBySlug(slug) {
  const res = await fetch(API_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: `
        query PostBySlug($slug: ID!) {
          post(id: $slug, idType: SLUG) {
            title
            content
            date
            featuredImage {
              node {
                sourceUrl
                altText
              }
            }
            seo {
              title
              metaDesc
              opengraphImage {
                sourceUrl
              }
            }
          }
        }
      `,
      variables: { slug },
    }),
    next: { revalidate: 60 },
  });
  const json = await res.json();
  return json.data.post;
}

デメリット? WordPress インストールをまだ維持します。セキュリティ更新、PHP バージョン管理、データベースバックアップ。すべてがあなたの皿に留まります。また、WordPress ホスティングに還対しても支払っています。

オプション2: 目的構築ヘッドレス CMS

これはほとんどの移行で推奨することです。コンテンツを API ファーストの配信のために一から構築された CMS に移動します。

CMS 価格 (2026年) 最適な用途 コンテンツモデリング API タイプ
Sanity 無料ティア、$15/ユーザー/月 Pro 複雑なコンテンツ、リアルタイムコラボレーション 優秀、コード定義 GROQ + GraphQL
Contentful 無料ティア、$300/月チーム エンタープライズ、大規模チーム 良好、UI 定義 REST + GraphQL
Storyblok 無料ティア、€106/月ビジネス ビジュアル編集、コンポーネント 素晴らしい、ビジュアル REST + GraphQL
Strapi v5 無料 (自己ホスト)、クラウド $29/月から 完全な制御、オープンソース 柔軟、UI 定義 REST + GraphQL
Payload CMS 3.0 無料 (自己ホスト) コード優先を望む開発者 優秀、コード定義 REST + GraphQL

Social Animal のチームが移行を処理している場合、通常は Sanity または Payload を推奨します。これらは開発者にコンテンツモデリングの最も制御を与えながら、エディターを満足させます。

オプション3: リポジトリ内の Markdown/MDX

開発者ブログとドキュメントサイトの場合、コンテンツを MDX ファイルとして Git リポジトリに保存することが最も単純なアプローチです。管理する CMS はなく、API 呼び出しはなく、コンテンツはコードと共にバージョン管理されます。しかし、これはコンテンツエディターが Git ワークフローに慣れている場合にのみ機能します。

コンテンツ移行戦略

WordPress からのエクスポート

組み込みの WordPress エクスポート (ツール → エクスポート) は XML ファイルを提供します。これは開始です、しかしそれは混乱しています。構造化された移行のために、私はカスタム WP-CLI スクリプトを使用します:

// export-content.php
<?php
$posts = get_posts([
    'post_type' => ['post', 'page', 'your_custom_type'],
    'posts_per_page' => -1,
    'post_status' => 'publish',
]);

$export = [];
foreach ($posts as $post) {
    $export[] = [
        'id' => $post->ID,
        'title' => $post->post_title,
        'slug' => $post->post_name,
        'content' => apply_filters('the_content', $post->post_content),
        'excerpt' => $post->post_excerpt,
        'date' => $post->post_date,
        'modified' => $post->post_modified,
        'author' => get_the_author_meta('display_name', $post->post_author),
        'categories' => wp_get_post_categories($post->ID, ['fields' => 'names']),
        'tags' => wp_get_post_tags($post->ID, ['fields' => 'names']),
        'featured_image' => get_the_post_thumbnail_url($post->ID, 'full'),
        'acf' => function_exists('get_fields') ? get_fields($post->ID) : [],
        'yoast' => [
            'title' => get_post_meta($post->ID, '_yoast_wpseo_title', true),
            'description' => get_post_meta($post->ID, '_yoast_wpseo_metadesc', true),
        ],
    ];
}

file_put_contents('export.json', json_encode($export, JSON_PRETTY_PRINT));

コンテンツの変換

WordPress コンテンツは Gutenberg ブロックマークアップを備えた HTML として保存されます。HTML を保持してレンダリングするか、CMS の構造化形式に変換するかを決定する必要があります。

Sanity の場合、@sanity/block-tools を使用して HTML を Portable Text に変換します。Contentful の場合、移行 CLI が豊富なテキスト変換を処理します。いずれにせよ、コンテンツのクリーンアップに時間をかけてください。WordPress コンテンツは [shortcodes]、インラインスタイル、および破損した HTML でいっぱいです。

// migrate-to-sanity.js
import { htmlToBlocks } from '@sanity/block-tools';
import { JSDOM } from 'jsdom';
import { Schema } from '@sanity/schema';

const schema = Schema.compile({ /* your schema */ });
const blockContentType = schema.get('post')
  .fields.find(f => f.name === 'body').type;

function convertPost(wpPost) {
  return {
    _type: 'post',
    title: wpPost.title,
    slug: { current: wpPost.slug },
    publishedAt: wpPost.date,
    body: htmlToBlocks(
      wpPost.content,
      blockContentType,
      { parseHtml: (html) => new JSDOM(html).window.document }
    ),
  };
}

画像移行

これをスキップしないでください。wp-content/uploads からすべての画像をダウンロードし、新しい CMS または CDN (Cloudinary、Vercel Blob Storage、S3) に再アップロードし、すべてのコンテンツ参照を更新します。通常、以下を実行するスクリプトを記述します:

  1. エクスポート JSON の画像 URL をクロール
  2. 各画像をダウンロード
  3. 新しいストレージにアップロード
  4. URL マッピングファイルを作成
  5. すべてのコンテンツで検索置換を実行

Next.js 15 でのフロントエンドの再構築

Next.js 15 (2024年後期から安定、2026年現在 15.2) はデフォルトで App Router を使用します。コンテンツ集約的なサイトに使用する構造:

app/
├── layout.tsx          # フォント、アナリティクス付きルートレイアウト
├── page.tsx            # ホームページ
├── blog/
│   ├── page.tsx        # ページネーション付きブログリスト
│   └── [slug]/
│       └── page.tsx    # 個別ブログ投稿
├── [slug]/
│   └── page.tsx        # 汎用ページ
├── sitemap.ts          # 動的サイトマップ生成
├── robots.ts           # robots.txt
└── not-found.tsx       # カスタム 404

ISR での静的生成

ほとんどのコンテンツページの場合、増分静的再生成は静的パフォーマンスと動的鮮度の甘い点です:

// app/blog/[slug]/page.tsx
import { getPostBySlug, getAllPostSlugs } from '@/lib/cms';
import { notFound } from 'next/navigation';

export async function generateStaticParams() {
  const slugs = await getAllPostSlugs();
  return slugs.map((slug) => ({ slug }));
}

export async function generateMetadata({ params }) {
  const post = await getPostBySlug(params.slug);
  if (!post) return {};
  return {
    title: post.seo?.title || post.title,
    description: post.seo?.description || post.excerpt,
    openGraph: {
      images: [post.featuredImage?.url],
    },
  };
}

export const revalidate = 3600; // 1時間ごとに再検証

export default async function BlogPost({ params }) {
  const post = await getPostBySlug(params.slug);
  if (!post) notFound();

  return (
    <article className="prose lg:prose-xl">
      <h1>{post.title}</h1>
      <time dateTime={post.date}>{formatDate(post.date)}</time>
      <PostBody content={post.body} />
    </article>
  );
}

リアルタイム更新が必要なサイト (ニュース、ライブコンテンツ) の場合、CMS からのウェブフック経由でオンデマンド再検証を使用します。ほとんどのヘッドレス CMS プラットフォームはコンテンツ公開時のウェブフックトリガーをサポートします。

Next.js 開発を検討していて、レンダリングのトレードオフについてさらに詳しく理解したい場合、別に書いています。

URL 構造と SEO の保持

これは譲歩不可能です。URL 構造を失うと、検索ランキングを失います。ピリオド。

リダイレクトマップ

WordPress は /2024/03/post-slug/ または /category/term/ のような URL パターンを使用します。Next.js サイトはおそらく /blog/post-slug を使用しています。リダイレクトマップを作成:

// next.config.js
module.exports = {
  async redirects() {
    return [
      // 日付ベース URL をクリーンスラッグに
      {
        source: '/:year(\\d{4})/:month(\\d{2})/:slug',
        destination: '/blog/:slug',
        permanent: true,
      },
      // カテゴリアーカイブ
      {
        source: '/category/:slug',
        destination: '/blog?category=:slug',
        permanent: true,
      },
      // ページネーション
      {
        source: '/page/:num',
        destination: '/blog?page=:num',
        permanent: true,
      },
      // フィード URL
      {
        source: '/feed',
        destination: '/rss.xml',
        permanent: true,
      },
    ];
  },
};

大規模サイト (1000+ リダイレクト) の場合、next.config.js リダイレクトの代わりに Vercel の vercel.json 設定またはミドルウェアを使用してください。next.config.js リダイレクトには約 1024 エントリのソフト制限があり、その後ビルド時間が増加し始めます。

構造化データ

Yoast などの WordPress プラグインは JSON-LD を自動生成します。これを複製する必要があります:

// components/structured-data.tsx
export function ArticleSchema({ post }) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: post.title,
    datePublished: post.date,
    dateModified: post.modified,
    author: {
      '@type': 'Person',
      name: post.author,
    },
    image: post.featuredImage?.url,
    publisher: {
      '@type': 'Organization',
      name: 'Your Site Name',
      logo: { '@type': 'ImageObject', url: '/logo.png' },
    },
  };

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  );
}

XML サイトマップ

Next.js 15 は動的サイトマップを簡潔にします:

// app/sitemap.ts
import { getAllPosts, getAllPages } from '@/lib/cms';

export default async function sitemap() {
  const posts = await getAllPosts();
  const pages = await getAllPages();

  return [
    { url: 'https://yoursite.com', lastModified: new Date() },
    ...pages.map((page) => ({
      url: `https://yoursite.com/${page.slug}`,
      lastModified: page.modified,
    })),
    ...posts.map((post) => ({
      url: `https://yoursite.com/blog/${post.slug}`,
      lastModified: post.modified,
    })),
  ];
}

Vercel への展開: 実際に機能する設定

プロジェクトセットアップ

npx create-next-app@latest my-migrated-site --typescript --tailwind --app
cd my-migrated-site
vercel link

環境変数

Git にコミットされた .env ファイルではなく、Vercel のダッシュボードで設定します:

CMS_API_URL=https://your-cms-api-endpoint
CMS_API_TOKEN=your-read-only-token
REVALIDATION_SECRET=a-random-string-for-webhook-auth
SITE_URL=https://yoursite.com

Vercel 設定

// vercel.json
{
  "crons": [
    {
      "path": "/api/revalidate-sitemap",
      "schedule": "0 */6 * * *"
    }
  ],
  "headers": [
    {
      "source": "/fonts/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
      ]
    }
  ]
}

オンデマンド再検証ウェブフック

// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const secret = request.headers.get('x-revalidation-secret');
  if (secret !== process.env.REVALIDATION_SECRET) {
    return NextResponse.json({ error: 'Invalid secret' }, { status: 401 });
  }

  const body = await request.json();
  const { slug, type } = body;

  if (type === 'post') {
    revalidatePath(`/blog/${slug}`);
    revalidatePath('/blog'); // リスティングも再検証
  } else {
    revalidatePath(`/${slug}`);
  }

  return NextResponse.json({ revalidated: true });
}

CMS ウェブフックを https://yoursite.com/api/revalidate にシークレットヘッダー付きで指します。コンテンツ更新は完全な再ビルドなしで数秒以内に表示されます。

パフォーマンスベンチマーク: 移行前後

これらは Social Animal が 2025~2026年に完成させた移行からの実数です:

メトリック WordPress (WP Engine) Next.js (Vercel) 改善
Lighthouse パフォーマンス 62-78 95-100 +30-40%
最大のコンテンフル ペイント 2.8-4.2秒 0.8-1.4秒 60-70% 高速
最初のバイトまでの時間 800ms-1.5秒 50-120ms 90%以上高速
累積レイアウトシフト 0.12-0.25 0.01-0.05 約 80% 削減
月額ホスティング費用 平均 $115/月 $20-40/月 60-80% 削減
ビルド時間 (500 ページ) N/A (動的) 45-90 秒 N/A
ページ/秒 (ISR) 15-30 req/秒 エッジから 10,000+ 桁違い

TTFB の改善だけでも移行の価値があります。WordPress はすべてのページを PHP とMySQL を通じて生成します。Vercel は世界中 300+ の場所のエッジノードから事前レンダリングされたページを数ミリ秒で提供します。

一般的な移行の落とし穴

落とし穴1: WordPress RSS フィードを忘れる。 人々がフィードを購読している場合、/feed//rss/ を新しい RSS エンドポイントにリダイレクトします。Next.js はデフォルトでフィードを生成しません。カスタムルートが必要です。

落とし穴2: WordPress ショートコードを見落とす。 WordPress からエクスポートされたコンテンツは [gallery][embed]、およびプラグイン固有のショートコードでいっぱいです。これらを解析して変換しないと、プレーンテキストとしてレンダリングされます。使用する各ショートコードタイプのトランスフォーマーを記述してください。

落とし穴3: WordPress コメントデータを無視する。 価値あるコメントスレッドがある場合は、それらを Disqus などのサービスに移行するか、カスタムコメントシステムを構築します。ほとんどの人はコメントをドロップするだけですが、まず利害関係者に確認してください。

落とし穴4: 内部リンクをテストしない。 WordPress コンテンツは絶対 URL (https://yoursite.com/old-path/) を使用した内部リンクでいっぱいです。これらは新しい URL 構造に相対パスに更新する必要があります。移行中のシンプルな正規表現検索置換がほとんどのケースを処理します。

落とし穴5: wp-content/uploads 参照を忘れる。 画像を移行した後でも、古いコンテンツは /wp-content/uploads/2024/03/image.jpg パスを参照する可能性があります。キャッチオール リダイレクトを設定するか、新しい画像 CDN にこれらをプロキシします。

これが圧倒的に感じられる場合、正直なところ正常です。適切な移行は複雑さによって 4~12 週間かかります。詳細は価格設定をチェックするか、プロジェクトに経験済みの手をかけたい場合は直接連絡してください。

よくある質問

WordPress から Next.js への移行にはどのくらい時間がかかりますか? 100~500 ページのサイトの場合、専任開発者で 4~8 週間を期待してください。カスタム投稿タイプ、e-コマース、または多言語コンテンツを備えた大規模サイトは 8~16 週間かかる可能性があります。コンテンツ移行自体は通常、作業の 20% です。残りの 80% はテンプレートの再構築、エッジケースの処理、およびすべての URL の QA テストです。

移行中に Google ランキングを失いますか? 変更されたすべての URL に対して 301 リダイレクトを適切に実装すれば失いません。メタタイトルと説明を保持し、新しいサイトマップを Google Search Console に送信し、ドメインが変更される場合は住所変更ツールを使用します。Google が改善されたコア Web バイタルを認識するため、2~4 週間の小さなランキング変動を期待してから回復または改善が期待できます。

Next.js で WordPress を CMS として使用し続けることはできますか? 絶対に。これは「ヘッドレス WordPress」と呼ばれ、一般的なアプローチです。WPGraphQL を使用してコンテンツを API として公開し、Next.js から使用します。エディターは知っている WordPress 管理画面を保持します。主な欠点は、WordPress インストレーション、セキュリティ更新、ホスティング、スタック全体を維持し続ける必要があります。

WordPress から Next.js への移行にはいくらかかりますか? 1 人の開発者で DIY: 100~300 時間の作業。エージェンシー移行: 通常 $15,000~$75,000 (複雑さによる)。進行中のホスティング費用は通常減少します。Vercel Pro ($20/ユーザー/月) 対マネージド WordPress ホスティング ($50~$300/月)。ROI はホスティング費用削減、より良いパフォーマンス (コンバージョン率を改善)、および低いメンテナンスオーバーヘッドから得られます。

Next.js 15 で Pages Router または App Router を使用すべきですか? App Router、完全に。2026年では、App Router は Server Components、ストリーミング、および並列ルートを備えた安定したデフォルトです。Pages Router は引き続き機能し、非推奨ではありませんが、新機能と最適化は App Router 優先です。Social Animal で実行するすべての移行は App Router を排他的に使用します。Next.js 開発機能でアプローチについてさらに詳しく読んでください。

フォームと動的機能はどうですか? Next.js API ルート (または App Router の Server Actions) はフォーム送信、検索、認証、およびサーバー側ロジックを処理します。連絡フォームの場合、Resend でのメール配信と共に Server Actions を使用できます。検索の場合、Algolia または Meilisearch を検討します。認証の場合、NextAuth.js (現在 Auth.js v5) がほとんどのユースケースをカバーします。

Vercel は Next.js をホスティングする唯一のオプションですか? いいえ。Netlify、AWS Amplify、Cloudflare Pages、または Node.js で自己ホストに Next.js をデプロイできます。ただし、Vercel は Next.js チームによって構築されており、統合はそれを示しています。ISR、エッジミドルウェア、画像最適化、およびアナリティクスはすべて Vercel で最適に機能します。2026年では、Vercel と代替案のギャップが狭まっていますが、Vercel は引き続き最も低い抵抗のパスです。

WooCommerce ストアを移行する必要がある場合はどうしますか? これはより大きなプロジェクトです。ほとんどのチームはストアフロントを Next.js に移行しながら、コマースバックエンドを Shopify (Storefront API 使用)、Medusa.js、または Saleor に移動します。Shopify の Hydrogen フレームワークは別のオプションですが、フロントエンドの完全な制御が必要な場合、Next.js と Shopify の API が最も実証済みのパスです。e-コマース移行がタイムラインに 4~8 週間追加されることを期待してください。