TL;DR

Migrar de WordPress para Next.js em 2026 oferece ganhos de desempenho mensuráveis: TTFB médio cai de 1.200ms para 85ms, peso da página diminui de 3,2 MB para 620 KB, e pontuações Lighthouse pulam de 42 para 94. O processo envolve exportar conteúdo via WP REST API para Supabase ou Payload CMS, migrar mídia para armazenamento de objetos, mapear cada URL para redirecionamentos 301, preservar dados SEO Yoast/RankMath na Next.js Metadata API e substituir plugins como Gravity Forms por Server Actions. Para sites WooCommerce, Stripe substitui toda a pilha de comércio. Espere 4-8 semanas para sites típicos com 100-500 páginas, com o maior investimento de tempo em testes de redirecionamento e reconstrução de templates.

Esta não é uma opinião precipitada. Venho construindo em WordPress há mais de 12 anos. Lancei sites de agência, plataformas de associação, lojas WooCommerce com receita mensal de seis dígitos e mais tipos de postagem personalizados do que gostaria de lembrar. Também migrei sites de produção para Next.js + Supabase. Aqui está cada detalhe técnico -- o que mapeio bem, o que não, e o que planejar.

Não vou fingir que WordPress é ruim. Não é. Ele alimenta 43% da web por uma boa razão. Mas para certos projetos -- sites onde desempenho é uma métrica de negócio, onde a superfície de segurança importa, onde você quer possuir seu pipeline de implantação -- Next.js é a ferramenta melhor. A migração, porém? É um campo minado se você não planejar.

Este guia cobre o processo exato que uso, com código real, armadilhas reais e avaliações honestas do que você ganhará e o que perderá.

Sumário

WordPress to Next.js Migration: A Complete Technical Guide

Migração de Conteúdo: WP REST API para Supabase ou Payload CMS

Cada migração WordPress começa aqui. Você tem posts, páginas, tipos de postagem personalizados, campos ACF, taxonomias -- anos de conteúdo que precisam ir para um lugar seguro.

Você tem duas opções sólidas para onde esse conteúdo vai:

  • Supabase -- se você quer um banco de dados que você controla totalmente, com segurança em nível de linha e API REST/GraphQL pronta para usar
  • Payload CMS -- se seu cliente precisa de uma experiência de edição visual que pareça familiar após WordPress

Para nossos projetos de desenvolvimento de headless CMS, avaliamos isso caso a caso. Payload vence quando editores precisam de autoatendimento. Supabase vence quando desenvolvedores são os gerenciadores de conteúdo primários ou quando você precisa dos dados para mais do que apenas um website.

Que estrutura de conteúdo devo preservar ao migrar de WordPress para Next.js?

Preserve metadados de postagem, taxonomias, campos personalizados e slugs de URL durante a migração. Suas postagens WordPress contêm anos de dados estruturados: categorias, tags, campos ACF, imagens destacadas e datas de publicação. Exporte tudo isso via WP REST API com o parâmetro _embed para obter URLs de mídia em uma única solicitação. Armazene versões HTML e Markdown do conteúdo -- HTML como fallback, Markdown para renderização MDX. Mapeie tipos de postagem personalizados para tabelas de banco de dados equivalentes ou coleções CMS em seu novo sistema.

O Script de Exportação

Aqui está o script Node.js que uso para extrair conteúdo da WP REST API, limpá-lo e inseri-lo no Supabase. Isso lida com posts, mas você duplicaria o padrão para páginas e CPTs (apenas altere o endpoint).

import { createClient } from '@supabase/supabase-js';
import TurndownService from 'turndown';

const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_SERVICE_KEY
);

const turndown = new TurndownService({
  headingStyle: 'atx',
  codeBlockStyle: 'fenced',
});

const WP_API = 'https://yoursite.com/wp-json/wp/v2';

async function fetchAllPosts() {
  let page = 1;
  let allPosts = [];
  let hasMore = true;

  while (hasMore) {
    const res = await fetch(
      `${WP_API}/posts?per_page=100&page=${page}&_embed`
    );

    if (!res.ok) break;

    const posts = await res.json();
    allPosts = allPosts.concat(posts);

    const totalPages = parseInt(res.headers.get('X-WP-TotalPages'));
    hasMore = page < totalPages;
    page++;
  }

  return allPosts;
}

async function migrateContent() {
  const posts = await fetchAllPosts();
  console.log(`Fetched ${posts.length} posts from WordPress`);

  const transformed = posts.map((post) => ({
    wp_id: post.id,
    title: post.title.rendered,
    slug: post.slug,
    content_html: post.content.rendered,
    content_markdown: turndown.turndown(post.content.rendered),
    excerpt: post.excerpt.rendered.replace(/<[^>]*>/g, '').trim(),
    published_at: post.date,
    status: post.status,
    featured_image:
      post._embedded?.['wp:featuredmedia']?.[0]?.source_url || null,
    categories:
      post._embedded?.['wp:term']?.[0]?.map((t) => t.name) || [],
    tags:
      post._embedded?.['wp:term']?.[1]?.map((t) => t.name) || [],
  }));

  const { data, error } = await supabase
    .from('posts')
    .upsert(transformed, { onConflict: 'wp_id' });

  if (error) {
    console.error('Migration failed:', error);
  } else {
    console.log(`Migrated ${transformed.length} posts to Supabase`);
  }
}

migrateContent();

Algumas coisas que aprendi da forma mais difícil:

  • Sempre use _embed em suas chamadas de WP REST API. Sem ele, você obtém IDs de mídia em vez de URLs, o que significa solicitações N+1 para resolver imagens destacadas.
  • Turndown converte HTML para Markdown -- isso é crítico se você planejar renderizar com MDX depois. Mantenha o HTML original também, como fallback.
  • Shortcodes não sobrevivem. WordPress renderiza alguns shortcodes via REST API, mas muitos (especialmente de plugins como WPBakery ou Elementor) chegam como texto de colchete bruto. Você precisa de uma estratégia de mapeamento shortcode-para-componente. Mantenho uma planilha.
  • ACF / Campos Personalizados: Se você estiver usando ACF, você precisará do plugin ACF to REST API habilitado, então os campos personalizados aparecem na propriedade acf de cada objeto de postagem.

Schema da Tabela Supabase

CREATE TABLE posts (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  wp_id INTEGER UNIQUE,
  title TEXT NOT NULL,
  slug TEXT UNIQUE NOT NULL,
  content_html TEXT,
  content_markdown TEXT,
  excerpt TEXT,
  published_at TIMESTAMPTZ,
  status TEXT DEFAULT 'publish',
  featured_image TEXT,
  categories TEXT[],
  tags TEXT[],
  seo_title TEXT,
  seo_description TEXT,
  og_image TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

Observe que incluí as colunas seo_title, seo_description e og_image. Você precisará delas na seção de migração de SEO abaixo.

Migração de Mídia: De wp-content para Supabase Storage

Esta é a parte que a maioria dos guias passa por cima, e é a parte que leva mais tempo. Um site WordPress com 12 anos pode facilmente ter 10.000+ arquivos em wp-content/uploads/.

A abordagem:

  1. Baixe todo o diretório wp-content/uploads/
  2. Carregue para Supabase Storage (ou Cloudflare R2, ou S3)
  3. Reescreva cada URL de mídia em seu conteúdo

Como migro arquivos de mídia do WordPress para armazenamento de objetos moderno?

Baixe todo o diretório wp-content/uploads/, carregue para Supabase Storage ou Cloudflare R2, depois reescreva todas as URLs de mídia em seu conteúdo. Use um script para buscar cada URL de imagem do seu conteúdo WordPress, carregue para armazenamento de objetos preservando a estrutura de diretório (2024/03/image.jpg), depois execute uma segunda passagem para substituir URLs antigas por novas URLs de armazenamento. Configure redirecionamentos curinga para sites externos que se vinculam diretamente às suas antigas URLs de imagem.

Script de Download e Upload

import { createClient } from '@supabase/supabase-js';
import fs from 'fs';
import path from 'path';
import fetch from 'node-fetch';

const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_SERVICE_KEY
);

const BUCKET = 'media';

async function migrateMedia(posts) {
  const urlRegex =
    /https?:\/\/yoursite\.com\/wp-content\/uploads\/[^\s"')]+/g;

  for (const post of posts) {
    const urls = post.content_html.match(urlRegex) || [];

    for (const url of urls) {
      try {
        const res = await fetch(url);
        const buffer = Buffer.from(await res.arrayBuffer());

        // Preserve directory structure: 2024/03/image.jpg
        const storagePath = url.replace(
          /https?:\/\/yoursite\.com\/wp-content\/uploads\//,
          ''
        );

        const { error } = await supabase.storage
          .from(BUCKET)
          .upload(storagePath, buffer, {
            contentType: res.headers.get('content-type'),
            upsert: true,
          });

        if (error) console.error(`Failed: ${storagePath}`, error);
        else console.log(`Uploaded: ${storagePath}`);
      } catch (e) {
        console.error(`Skipped: ${url}`, e.message);
      }
    }
  }
}

async function rewriteUrls() {
  const { data: posts } = await supabase.from('posts').select('*');
  const supabaseBase = `${process.env.SUPABASE_URL}/storage/v1/object/public/${BUCKET}`;

  for (const post of posts) {
    const updated = post.content_html.replace(
      /https?:\/\/yoursite\.com\/wp-content\/uploads\//g,
      `${supabaseBase}/`
    );

    await supabase
      .from('posts')
      .update({
        content_html: updated,
        content_markdown: turndown.turndown(updated),
      })
      .eq('id', post.id);
  }
}

O Ganho de Desempenho de Imagem

É aqui que a migração realmente compensa. WordPress serve uploads originais -- geralmente PNGs 3000×2000px que alguém fez upload de sua câmera DSLR. Mesmo com um plugin como ShortPixel, você ainda está servindo imagens através de PHP.

O componente <Image> do Next.js com next/image faz negociação automática de formato (WebP/AVIF), dimensionamento responsivo e carregamento lento. Os números de nossa última migração:

Métrica WordPress Next.js + Componente Image
Peso médio de imagem por página 2,1 MB 380 KB
Requisições de imagem 12 por página 6 por página (carregamento lento)
Formato JPEG/PNG WebP (AVIF onde suportado)
Cumulative Layout Shift 0,18 0,02

Isso não é erro de digitação. O peso médio de imagem caiu de 2,1 MB para 380 KB. E isso foi sem fazer re-upload de arquivos otimizados -- apenas deixando next/image fazer seu trabalho.

import Image from 'next/image';

export function PostImage({ src, alt }: { src: string; alt: string }) {
  return (
    <Image
      src={src}
      alt={alt}
      width={800}
      height={450}
      sizes="(max-width: 768px) 100vw, 800px"
      quality={80}
      placeholder="blur"
      blurDataURL="data:image/jpeg;base64,..." // gerar no tempo de construção
    />
  );
}

Estrutura de URLs: Mapeando Cada URL Antiga

É aqui que as migrações morrem. Um redirecionamento perdido significa um 404 para uma página que Google indexou por anos. Trato mapeamento de URL com a mesma seriedade que uma migração de banco de dados -- teste, verifique e depois verifique novamente.

Qual é a forma correta de lidar com redirecionamentos de URL durante a migração do WordPress?

Exporte cada URL publicada do WordPress, referencie cruzada com URLs indexadas do Google Search Console e implemente redirecionamentos 301 para todas elas. Consulte sua tabela wp_posts para todas as URLs publicadas, exporte as URLs indexadas do GSC e crie um mapa de redirecionamento. Use redirecionamentos next.config.js para menos de 50 URLs, um arquivo JSON para 50-1.024 URLs, ou middleware para sites que excedem o limite de 1.024 redirecionamentos do Vercel. Inclua redirecionamentos curinga para páginas de categoria, paginação e caminhos wp-content/uploads.

O Processo de Mapeamento

Primeiro, exporte cada URL do WordPress. Puxo diretamente do banco de dados:

SELECT
  CONCAT('/', post_name, '/') AS old_url,
  post_type,
  post_status
FROM wp_posts
WHERE post_status = 'publish'
  AND post_type IN ('post', 'page', 'product')
ORDER BY post_type, post_name;

Então faça referência cruzada com as URLs indexadas do Google Search Console. GSC geralmente mostra URLs que não existem mais em seu banco de dados -- antigas páginas de categoria, URLs de paginação, páginas de anexo. Você precisa de redirecionamentos para todas elas.

Redirecionamentos next.config.js

Para sites com menos de 50 redirecionamentos, inclua-os:

// next.config.js
module.exports = {
  async redirects() {
    return [
      {
        source: '/2019/03/old-post-slug/',
        destination: '/blog/old-post-slug',
        permanent: true,
      },
      {
        source: '/category/:slug',
        destination: '/blog/category/:slug',
        permanent: true,
      },
      {
        source: '/product/:slug',
        destination: '/shop/:slug',
        permanent: true,
      },
    ];
  },
};

Para 200+ Redirecionamentos: Use um Arquivo JSON

Depois que você passar de alguns cento, manter redirecionamentos embutidos é miserável. Eu uso um arquivo JSON:

// redirects.json
[
  {
    "source": "/2018/01/my-old-post/",
    "destination": "/blog/my-old-post",
    "permanent": true
  },
  {
    "source": "/about-us/",
    "destination": "/about",
    "permanent": true
  },
  {
    "source": "/wp-content/uploads/:path*",
    "destination": "https://yourbucket.supabase.co/storage/v1/object/public/media/:path*",
    "permanent": true
  }
]
// next.config.js
const redirectsList = require('./redirects.json');

module.exports = {
  async redirects() {
    return redirectsList;
  },
};

O redirecionamento curinga para wp-content/uploads é crítico. Haverá sites externos se vinculando diretamente às suas imagens. Não perca esses backlinks.

Importante: Vercel tem um limite de 1.024 redirecionamentos em next.config.js. Para sites com mais do que isso, use middleware:

// middleware.ts
import { NextResponse } from 'next/server';
import redirects from './redirects.json';

const redirectMap = new Map(
  redirects.map((r) => [r.source, r])
);

export function middleware(request) {
  const redirect = redirectMap.get(request.nextUrl.pathname);
  if (redirect) {
    return NextResponse.redirect(
      new URL(redirect.destination, request.url),
      redirect.permanent ? 308 : 307
    );
  }
}

WordPress to Next.js Migration: A Complete Technical Guide - architecture

Migração de SEO: Dados Yoast e RankMath para Next.js Metadata

Se você estivesse usando Yoast ou RankMath, tem anos de títulos meta personalizados, descrições e dados Open Graph armazenados na tabela wp_postmeta. Não os perca.

Como preservo dados Yoast SEO ao migrar para Next.js?

Exporte títulos meta Yoast, descrições e imagens Open Graph de wp_postmeta, armazene-os em seu novo banco de dados e depois renderize-os usando Next.js Metadata API. Consulte wp_postmeta pelos campos _yoast_wpseo_title, _yoast_wpseo_metadesc e _yoast_wpseo_opengraph-image. Importe esses dados em colunas SEO dedicadas em sua tabela de posts. Use generateMetadata no Next.js App Router para renderizar esses dados como tags meta adequadas e markup Open Graph.

Exportando Dados de SEO

SELECT
  p.post_name AS slug,
  MAX(CASE WHEN pm.meta_key = '_yoast_wpseo_title' THEN pm.meta_value END) AS seo_title,
  MAX(CASE WHEN pm.meta_key = '_yoast_wpseo_metadesc' THEN pm.meta_value END) AS seo_description,
  MAX(CASE WHEN pm.meta_key = '_yoast_wpseo_opengraph-image' THEN pm.meta_value END) AS og_image
FROM wp_posts p
JOIN wp_postmeta pm ON p.ID = pm.post_id
WHERE p.post_status = 'publish'
GROUP BY p.ID, p.post_name;

Para RankMath, troque as chaves meta: rank_math_title, rank_math_description, rank_math_facebook_image.

Importe esses dados para a tabela posts Supabase nas colunas SEO que definimos anteriormente.

Next.js Metadata API

Com o App Router, metadados são um cidadão de primeira classe:

// app/blog/[slug]/page.tsx
import { supabase } from '@/lib/supabase';
import { Metadata } from 'next';

export async function generateMetadata(
  { params }: { params: { slug: string } }
): Promise<Metadata> {
  const { data: post } = await supabase
    .from('posts')
    .select('title, seo_title, seo_description, og_image')
    .eq('slug', params.slug)
    .single();

  return {
    title: post.seo_title || post.title,
    description: post.seo_description,
    openGraph: {
      title: post.seo_title || post.title,
      description: post.seo_description,
      images: post.og_image ? [{ url: post.og_image }] : [],
    },
  };
}

Markup de Schema como Componentes de Servidor JSON-LD

Plugins do WordPress geram schema automaticamente. Em Next.js, você constrói você mesmo -- o que na verdade lhe dá mais controle:

// components/ArticleSchema.tsx
export function ArticleSchema({ post }) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: post.title,
    datePublished: post.published_at,
    dateModified: post.updated_at || post.published_at,
    author: {
      '@type': 'Organization',
      name: 'Your Company',
    },
    image: post.og_image,
    description: post.seo_description,
  };

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

Sitemaps Dinâmicos

// app/sitemap.ts
import { supabase } from '@/lib/supabase';

export default async function sitemap() {
  const { data: posts } = await supabase
    .from('posts')
    .select('slug, published_at')
    .eq('status', 'publish');

  return posts.map((post) => ({
    url: `https://yoursite.com/blog/${post.slug}`,
    lastModified: post.published_at,
    changeFrequency: 'monthly',
    priority: 0.8,
  }));
}

Isso gera no tempo de construção para sites estáticos ou sob demanda para dinâmicos. Sem plugin, sem arquivos de template XML, sem problemas de cache.

Formulários: Gravity Forms para Server Actions

Gravity Forms é um dos melhores plugins WordPress já criados. Também custa $259/ano pela licença Elite, e cada formulário carrega 200KB+ de JavaScript.

Aqui está o substituto. São cerca de 20 linhas de código por formulário.

Exporte Entradas Existentes

Primeiro, exporte suas entradas de Gravity Forms como CSV do painel de administração WordPress. Armazene no Supabase para registros históricos se necessário.

Formulário de Contato de Server Action

// app/contact/page.tsx
export default function ContactPage() {
  async function submitForm(formData: FormData) {
    'use server';

    const { createClient } = await import('@supabase/supabase-js');
    const supabase = createClient(
      process.env.SUPABASE_URL!,
      process.env.SUPABASE_SERVICE_KEY!
    );

    const entry = {
      name: formData.get('name') as string,
      email: formData.get('email') as string,
      message: formData.get('message') as string,
      submitted_at: new Date().toISOString(),
    };

    // Validar
    if (!entry.name || !entry.email || !entry.message) {
      throw new Error('All fields required');
    }

    await supabase.from('form_submissions').insert(entry);

    // Opcional: enviar email de notificação via Resend
    await fetch('https://api.resend.com/emails', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.RESEND_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        from: 'forms@yoursite.com',
        to: 'team@yoursite.com',
        subject: `New contact: ${entry.name}`,
        html: `<p>${entry.message}</p><p>From: ${entry.email}</p>`,
      }),
    });
  }

  return (
    <form action={submitForm}>
      <input name="name" type="text" required placeholder="Name" />
      <input name="email" type="email" required placeholder="Email" />
      <textarea name="message" required placeholder="Message" />
      <button type="submit">Send</button>
    </form>
  );
}

Sem plugin. Sem carga útil JavaScript para o formulário em si (é um formulário HTML nativo com uma server action). Melhoria progressiva -- funciona sem JavaScript habilitado.

Para formulários mais complexos (multietapas, uploads de arquivo, campos condicionais), usamos React Hook Form no lado do cliente com o mesmo padrão de server action. A percepção-chave: você não precisa de um plugin de formulário quando tem um banco de dados e uma API.

WooCommerce para Stripe

Esta é a parte mais difícil de qualquer migração WordPress. WooCommerce não é apenas um plugin -- é uma plataforma de comércio com produtos, variações, inventário, pedidos, assinaturas, cupons, impostos e regras de envio. Você não está migrando um recurso. Você está substituindo uma plataforma.

Como migro produtos WooCommerce para Stripe?

Exporte produtos via API REST WooCommerce ou CSV e crie produtos correspondentes na API Products Stripe com preços. Para sites com menos de 500 produtos, envie diretamente para Stripe usando sua API: crie um produto com nome, descrição e imagens, depois crie um objeto de preço vinculado a esse produto. Armazene o ID do produto WooCommerce em metadados Stripe para referência. Use Stripe Checkout Sessions para processamento de pagamento e webhooks para rastrear pedidos em seu banco de dados.

Migração de Produto

Exporte produtos do WooCommerce via CSV ou API REST. Você tem duas opções de destino:

Abordagem Melhor Para Trocas
Tabela de produtos Supabase Vitrines personalizadas, filtragem complexa Você gerencia lógica de inventário
API Stripe Products Catálogos simples, negócios de assinatura Stripe gerencia preços, você gerencia exibição

Para a maioria dos sites com menos de 500 produtos, envio diretamente para Stripe Products:

import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

async function migrateProducts(wooProducts) {
  for (const product of wooProducts) {
    const stripeProduct = await stripe.products.create({
      name: product.name,
      description: product.short_description,
      images: [product.images[0]?.src].filter(Boolean),
      metadata: {
        woo_id: String(product.id),
        slug: product.slug,
        sku: product.sku,
      },
    });

    await stripe.prices.create({
      product: stripeProduct.id,
      unit_amount: Math.round(parseFloat(product.price) * 100),
      currency: 'usd',
    });

    console.log(`Created: ${product.name} → ${stripeProduct.id}`);
  }
}

Checkout com Stripe Checkout Sessions

// app/api/checkout/route.ts
import { NextResponse } from 'next/server';
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(request: Request) {
  const { priceId, quantity = 1 } = await request.json();

  const session = await stripe.checkout.sessions.create({
    mode: 'payment',
    payment_method_types: ['card'],
    line_items: [{ price: priceId, quantity }],
    success_url: `${process.env.NEXT_PUBLIC_URL}/order/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/shop`,
  });

  return NextResponse.json({ url: session.url });
}

Assinaturas

Se você estiver no WooCommerce Subscriptions ($239/ano), mude para Stripe Billing. Altere mode: 'payment' para mode: 'subscription' e certifique-se de que seus preços tenham recurring definido. Pronto. Stripe lida com períodos de teste, rateamento e dunning.

Rastreamento de Pedidos via Webhooks

// app/api/webhooks/stripe/route.ts
import { headers } from 'next/headers';
import Stripe from 'stripe';
import { supabase } from '@/lib/supabase';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(request: Request) {
  const body = await request.text();
  const sig = headers().get('stripe-signature')!;

  const event = stripe.webhooks.constructEvent(
    body,
    sig,
    process.env.STRIPE_WEBHOOK_SECRET!
  );

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object as Stripe.Checkout.Session;

    await supabase.from('orders').insert({
      stripe_session_id: session.id,
      customer_email: session.customer_details?.email,
      amount_total: session.amount_total,
      status: 'completed',
    });
  }

  return new Response('OK', { status: 200 });
}

As taxas de transação do Stripe são 2,9% + $0,30 por transação. Compare com WordPress onde você está pagando por hospedagem ($30-100/mês por WP gerenciado), o plugin Subscriptions ($239/ano), um plugin de gateway de pagamento e provavelmente alguns outros complementos. A matemática funciona rapidamente.

Para migrações de comércio complexas, oferecemos isso como parte de nossos serviços de desenvolvimento Next.js -- é um dos pedidos mais comuns que recebemos.

Monitoramento Pós-Migração

Lançar não é o fim. As duas primeiras semanas após a migração são críticas.

Google Search Console

  • Envie seu novo sitemap imediatamente
  • Use a ferramenta de Inspeção de URL para solicitar indexação de seus 20 principais páginas
  • Monitore o relatório de Cobertura diariamente na primeira semana -- procure por picos em 404s
  • Verifique o relatório "Indexação de página" para qualquer página presa em "Descoberta -- atualmente não indexada"

Comparação de Analíticos

Configure um painel que compare semana a semana:

  • Sessões totais
  • Tráfego de pesquisa orgânica especificamente
  • Taxa de rejeição por página
  • Taxa de conversão (envios de formulário, compras)

Um pequeno declínio de tráfego na semana um é normal. Se não se recuperar até a semana três, algo deu errado com redirecionamentos ou indexação.

Auditorias do Lighthouse

Execute Lighthouse em cada template importante (página inicial, postagem de blog, página de produto, página de contato). Direcione-se para:

  • Desempenho: 90+
  • Acessibilidade: 95+
  • Melhores Práticas: 95+
  • SEO: 100

Em nossa última migração -- um site de conteúdo de 400 páginas -- fomos de uma pontuação de desempenho média do Lighthouse de 38 no WordPress para 96 no Next.js implantado no Vercel. Isso não é cherry-picked. Isso é a média.

Quando Permanecer no WordPress

Aqui é a parte onde perco alguns de vocês, mas é a seção mais importante neste guia.

Não migre se:

  • Você tem um blog simples ou site de brochura com menos de 20 páginas
  • Sua equipe não é técnica e depende do painel de administração WordPress para atualizações diárias
  • Suas pontuações do Lighthouse já estão em 70+ e você não tem necessidades de negócio críticas de desempenho
  • Você não tem problemas de segurança e sua hospedagem é estável
  • Seus custos totais de plugin estão abaixo de $200/ano
  • Você não tem um desenvolvedor (ou orçamento para um) para manter um site Next.js

WordPress com um bom host (Cloudways, Kinsta), um tema sólido e plugins mínimos está bom. Na verdade, é mais do que bom -- é battle-tested, bem documentado e compreendido por milhões de desenvolvedores.

A migração faz sentido quando:

  • Desempenho está diretamente ligado à receita (e-commerce, sites de marketing SaaS)
  • Você está gastando $500+/mês em hospedagem gerenciada e plugins de segurança
  • Sua equipe de desenvolvimento já está escrevendo React
  • Você precisa de um pipeline de implantação com builds de prévia, ambientes de preparação e reversões
  • Superfície de segurança é uma preocupação genuína (governo, saúde, finanças)

Digo isso porque confiança importa mais do que uma venda. Se você não tem certeza se a migração vale a pena, entre em contato conosco e daremos uma avaliação honesta.

Benchmarks de Desempenho: Antes e Depois

De nossas últimas cinco migrações em 2024-2025:

Métrica WordPress (Média) Next.js (Média) Mudança
TTFB 1.200ms 85ms 14x mais rápido
LCP 3,8s 0,9s 4,2x mais rápido
Peso Total da Página 3,2 MB 620 KB 5x mais leve
Requisições por Página 47 11 77% menos
Desempenho do Lighthouse 42 94 +52 pontos
Custo Mensal de Hospedagem $75 $20 (Vercel Pro) 73% economizado
Taxa de Aprovação Core Web Vitals 31% das páginas 100% das páginas

Esses são números reais de sites de produção. Os sites WordPress estavam rodando hospedagem gerenciada (WP Engine e Kinsta), cache otimizado e plugins de otimização de imagem. Eles não eram negligenciados -- eram sites mantidos que simplesmente atingiram o limite do que WordPress pode entregar.

Se você está interessado no que é possível com frameworks modernos, confira nossas capacidades de desenvolvimento Astro também -- para sites ricos em conteúdo com interatividade mínima, Astro pode entregar cargas úteis ainda menores do que Next.js.

Perguntas Frequentes

Quanto tempo leva uma migração de WordPress para Next.js?

Para um site típico com 100-500 páginas, espere 4-8 semanas de tempo de desenvolvimento. Sites de brochura simples podem ser feitos em 2-3 semanas. Lojas WooCommerce complexas com milhares de produtos podem levar 10-12 semanas. A migração de conteúdo em si é rápida -- é reconstruir os templates frontend e testar cada redirecionamento que leva tempo.

Perderei classificações de SEO ao migrar de WordPress para Next.js?

Não se você lidar com redirecionamentos e metadados corretamente. As peças críticas são: redirecionamentos 301 para cada URL antiga, migração de todos os títulos e descrições meta Yoast/RankMath, preservação da estrutura do mapa do site e submissão do novo mapa do site ao Google Search Console imediatamente. Vimos sites se recuperarem para tráfego pré-migração dentro de 1-2 semanas, com crescimento orgânico significativo até o mês três devido aos Core Web Vitals melhorados.

Posso usar WordPress como um CMS headless com Next.js?

Sim, e é uma abordagem popular. Você mantém WordPress como back-end de conteúdo, usando a WP REST API ou WPGraphQL, e Next.js como frontend. Isso preserva a experiência de edição familiar enquanto obtém desempenho Next.js. A desvantagem é que você ainda está mantendo uma instalação WordPress com sua segurança e sobrecarga de atualização. Geralmente recomendamos Payload CMS ou Sanity para novos projetos a menos que a equipe esteja profundamente investida em fluxos de trabalho WordPress.

Quanto custa migrar de WordPress para Next.js?

DIY com tempo de desenvolvedor: grátis em ferramentas, mas orçamento 80-200 horas de tempo de desenvolvimento. Custo de agência: tipicamente $10.000-$50.000 dependendo da complexidade do site, número de páginas, recursos de e-commerce e funcionalidade personalizada. Verifique nossa página de preços para especificidades em nossos pacotes. O ROI geralmente vem de custos de hospedagem reduzidos ($50-100/mês economizados), taxas de licença de plugin eliminadas e taxas de conversão aumentadas a partir de melhor desempenho.

O que acontece com meus plugins WordPress após a migração?

Cada plugin precisa de um equivalente Next.js. Contact Form 7 ou Gravity Forms se torna uma Server Action. Yoast SEO se torna a Next.js Metadata API. WooCommerce se torna Stripe. Google Analytics permanece o mesmo (apenas mova o trecho de rastreamento). Alguns plugins como Wordfence se tornam desnecessários já que não há WordPress para atacar. Faça um inventário completo de seus plugins antes de começar -- qualquer plugin sem uma estratégia clara de substituição é um risco.

Devo migrar para Next.js ou Astro do WordPress?

Depende de suas necessidades de interatividade. Next.js é melhor para sites com recursos dinâmicos -- autenticação de usuário, e-commerce, painéis, dados em tempo real. Astro é melhor para sites ricos em conteúdo que são principalmente estáticos -- blogs, documentação, sites de marketing. Astro envia zero JavaScript por padrão, o que significa tamanhos de página ainda menores. Trabalhamos com ambos -- veja nossas páginas de desenvolvimento Astro e desenvolvimento Next.js para detalhes.

Posso migrar assinaturas WooCommerce para Stripe?

Sim, mas requer manuseio cuidadoso de assinantes ativos. Você precisará criar clientes e assinaturas no Stripe, então comunicar a mudança de faturamento aos clientes. Stripe Billing lida com períodos de teste, rateamento, lógica de retenção de pagamento falhado e fluxos de cancelamento. A migração em si é um script único, mas testá-la contra cenários de assinatura reais é onde o tempo vai. Orçamento tempo extra para isso se você tiver mais de 100 assinantes ativos.

Qual é a melhor hospedagem para Next.js após migrar do WordPress?

Vercel é a escolha padrão -- é construída pela equipe que faz Next.js, e o nível gratuito lida com a maioria dos sites de marketing. Vercel Pro é $20/mês para equipes. Alternativas incluem Netlify, Cloudflare Pages (excelente para desempenho de borda) e auto-hospedagem com Docker em um VPS se você quer controle total. Todas essas são significativamente mais baratas do que hospedagem WordPress gerenciada para níveis de tráfego equivalentes.