Migração de WordPress para Next.js no Vercel: Guia 2026
Migrei mais de uma dúzia de sites WordPress para Next.js nos últimos três anos. Alguns correram suavemente. Alguns me fizeram questionar minhas escolhas de carreira às 2 da manhã de uma terça-feira. A diferença entre esses dois resultados quase sempre se resumiu ao planejamento — especificamente, entender o que WordPress estava realmente fazendo pelo site antes de arrancá-lo.
Este guia é tudo que gostaria que alguém tivesse me entregue antes da minha primeira migração. Cobriremos a jornada completa: avaliar se você deveria sequer migrar, escolher seu CMS headless, mover conteúdo, reconstruir templates, lidar com SEO sem perder rankings e fazer deployment no Vercel com uma configuração que não desabará sob picos de tráfego.
Vamos começar.

Índice
- Por que migrar do WordPress para Next.js em 2026?
- Auditoria pré-migração: O que WordPress está realmente fazendo
- Escolhendo seu backend CMS headless
- Estratégia de migração de conteúdo
- Reconstruindo seu frontend em Next.js 15
- Estrutura de URL e preservação de SEO
- Deployment no Vercel: Configuração que realmente funciona
- Benchmarks de performance: Antes e depois
- Armadilhas comuns de migração
- FAQ
Por que migrar do WordPress para Next.js em 2026?
Sejamos honestos — WordPress ainda alimenta aproximadamente 40% da web em 2026. Não vai embora tão cedo. Mas as razões para sair ficaram mais convincentes:
Teto de performance. Mesmo com plugins de cache agressivo (WP Rocket, W3 Total Cache), a maioria dos sites WordPress atinge um limite em torno de 70-80 em scores de performance do Lighthouse. Excesso de plugins, PHP renderizando bloqueios e queries de banco de dados em cada carregamento de página criam overhead que nenhuma quantidade de otimização consegue eliminar completamente.
Superfície de segurança. WordPress teve 149 vulnerabilidades documentadas em 2025 em core e plugins populares. Cada plugin é um vetor de ataque. Cada atualização de tema é um possível break. Se você está rodando WooCommerce, a superfície dobra.
Experiência do desenvolvedor. Se seu time conhece React, construir em templates PHP se sente como escrever com sua mão não-dominante. O App Router do Next.js 15, Server Components e cache integrado oferecem um workflow de desenvolvimento moderno que WordPress não consegue igualar.
Custo em escala. Hospedagem WordPress gerenciada (WP Engine, Kinsta) custa $30-$300/mês para performance decente. O plano Pro do Vercel a $20/usuário/mês com edge functions e scaling automático frequentemente custa menos enquanto perfoma melhor.
Dito isto — não migre apenas porque é tendência. Se seu site é um blog simples com 50 posts e seu cliente o atualiza semanalmente via admin do WordPress, uma migração pode criar mais problemas do que resolve. Os melhores candidatos para migração são:
- Sites com 500+ páginas que precisam de melhor performance
- Times que querem desenvolvimento baseado em componentes
- Sites onde conflitos de plugins estão causando pesadelos de manutenção
- Sites de e-commerce atingindo limites de performance do WooCommerce
- Sites de marketing que precisam de A/B testing e personalização na edge
Auditoria pré-migração: O que WordPress está realmente fazendo
É aqui que a maioria das migrações dá errado. As pessoas pensam que estão migrando um blog, mas WordPress está realmente manipulando formulários de contato, redirects, otimização de imagens, busca, comentários, autenticação, cron jobs e quinze outras coisas enterradas em configurações de plugin.
Antes de escrever uma única linha de código Next.js, documente tudo:
Inventário de plugins
Exporte sua lista de plugins e categorize cada um:
wp plugin list --status=active --format=csv > active-plugins.csv
Para cada plugin, responda: O que isto faz, e o que o substitui no ecossistema Next.js?
| Plugin WordPress | Função | Substituição Next.js |
|---|---|---|
| Yoast SEO | Meta tags, sitemaps, schema | next-seo + rota sitemap.xml customizada |
| WP Rocket | Cache, minificação | Vercel Edge Cache + Next.js integrado |
| Contact Form 7 | Manipulação de formulário | React Hook Form + rota API ou Formspree |
| Wordfence | Segurança | Não necessário (sem superfície admin) |
| WPML | Multilíngue | next-intl ou roteamento i18n integrado |
| WooCommerce | E-commerce | Shopify Storefront API ou Saleor |
| Advanced Custom Fields | Modelos de conteúdo customizado | Modelagem de conteúdo do seu CMS headless |
| Redirection | URL redirects | next.config.js redirects ou config Vercel |
| WP Cron | Tarefas agendadas | Vercel Cron Jobs ou serviço separado |
| Imagify | Otimização de imagem | next/image com Vercel Image Optimization |
Inventário de conteúdo
Contabilize e categorize seu conteúdo:
SELECT post_type, post_status, COUNT(*)
FROM wp_posts
GROUP BY post_type, post_status;
Não esqueça: custom post types, termos de taxonomy, perfis de usuário, estruturas de menu, configurações de widget e valores de option. Aquele site WordPress "simples" provavelmente tem mais tipos de conteúdo do que você pensa.
Mapeamento de URL
Exporte toda URL. Toda uma. Use Screaming Frog ou um simples crawl de sitemap:
curl -s https://seusite.com/sitemap_index.xml | \
grep -oP '<loc>\K[^<]+' | \
xargs -I {} curl -s {} | \
grep -oP '<loc>\K[^<]+' > all-urls.txt
Este arquivo é ouro. Você o usará para mapeamento de redirect, preservação de SEO e testes de QA após migração.

Escolhendo seu backend CMS headless
Você precisa de um lugar para armazenar e gerenciar conteúdo. Os três caminhos mais comuns em 2026:
Opção 1: WordPress como CMS headless
Sim, você pode manter WordPress como o backend e usar Next.js como frontend. WPGraphQL (agora em v2.1) torna isso surpreendentemente viável. Seus editores mantêm a interface admin familiar. Você ganha um frontend moderno.
// 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;
}
A desvantagem? Você ainda mantém uma instalação WordPress. Atualizações de segurança, gerenciamento de versão PHP, backups de banco de dados — tudo continua em seu prato. E você continua pagando pela hospedagem WordPress.
Opção 2: CMS headless construído para propósito
Isso é o que recomendo para a maioria das migrações. Mova seu conteúdo para um CMS que foi construído do zero para entrega API-first.
| CMS | Preço (2026) | Melhor para | Modelagem de conteúdo | Tipo API |
|---|---|---|---|---|
| Sanity | Tier grátis, $15/usuário/mês Pro | Conteúdo complexo, colaboração em tempo real | Excelente, definida em código | GROQ + GraphQL |
| Contentful | Tier grátis, $300/mês Team | Enterprise, times grandes | Boa, definida em UI | REST + GraphQL |
| Storyblok | Tier grátis, €106/mês Business | Edição visual, componentes | Ótima, visual | REST + GraphQL |
| Strapi v5 | Grátis (auto-hospedado), Cloud a partir de $29/mês | Controle total, open source | Flexível, definida em UI | REST + GraphQL |
| Payload CMS 3.0 | Grátis (auto-hospedado) | Desenvolvedores que querem code-first | Excelente, definida em código | REST + GraphQL |
Se seu time na Social Animal está lidando com a migração, tipicamente recomendamos Sanity ou Payload para desenvolvimento de CMS headless — eles dão aos desenvolvedores o máximo controle sobre modelagem de conteúdo enquanto mantêm editores felizes.
Opção 3: Markdown/MDX no repositório
Para blogs de desenvolvedores e sites de documentação, armazenar conteúdo como arquivos MDX em seu repositório Git é a abordagem mais simples. Nenhum CMS para gerenciar, sem chamadas de API, conteúdo versionado junto com código. Mas isso só funciona se seus editores de conteúdo estão confortáveis com workflows Git.
Estratégia de migração de conteúdo
Exportando do WordPress
A exportação integrada do WordPress (Tools → Export) oferece um arquivo XML. É um começo, mas é bagunçado. Para migração estruturada, uso um script WP-CLI customizado:
// 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));
Transformando conteúdo
Conteúdo WordPress é armazenado como HTML com markup de bloco Gutenberg. Você precisará decidir: manter o HTML e renderizá-lo, ou converter para o formato estruturado do seu CMS?
Para Sanity, uso @sanity/block-tools para converter HTML para Portable Text. Para Contentful, seu CLI de migração manipula conversão de rich text. De qualquer forma, reserve tempo para limpeza de conteúdo — conteúdo WordPress está cheio de [shortcodes], estilos inline e HTML quebrado que precisa ser limpo.
// migrate-to-sanity.js
import { htmlToBlocks } from '@sanity/block-tools';
import { JSDOM } from 'jsdom';
import { Schema } from '@sanity/schema';
const schema = Schema.compile({ /* seu 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 }
),
};
}
Migração de imagens
Não pule isso. Baixe cada imagem de wp-content/uploads, re-upload para seu CMS ou uma CDN (Cloudinary, Vercel Blob Storage, S3) e atualize todas as referências de conteúdo. Tipicamente escrevo um script que:
- Rastreia a exportação JSON para URLs de imagem
- Baixa cada imagem
- Upload para o novo armazenamento
- Cria um arquivo de mapeamento de URL
- Executa find-and-replace em todo conteúdo
Reconstruindo seu frontend em Next.js 15
Next.js 15 (estável desde final de 2024, com 15.2 atual em 2026) usa o App Router por padrão. Aqui está a estrutura que uso para sites com muito conteúdo:
app/
├── layout.tsx # Layout raiz com fontes, analytics
├── page.tsx # Homepage
├── blog/
│ ├── page.tsx # Listagem de blog com paginação
│ └── [slug]/
│ └── page.tsx # Posts de blog individuais
├── [slug]/
│ └── page.tsx # Páginas genéricas
├── sitemap.ts # Geração dinâmica de sitemap
├── robots.ts # robots.txt
└── not-found.tsx # Custom 404
Geração estática com ISR
Para a maioria das páginas de conteúdo, Incremental Static Regeneration é o ponto ideal — performance estática com freshness dinâmica:
// 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; // Revalidar a cada hora
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>
);
}
Para sites necessitando atualizações em tempo real (notícias, conteúdo ao vivo), use revalidação on-demand via webhooks do seu CMS. A maioria das plataformas CMS headless suportam triggers de webhook na publicação de conteúdo.
Se você está olhando para desenvolvimento Next.js e quer entender melhor os trade-offs de renderização, escrevemos sobre isso separadamente.
Estrutura de URL e preservação de SEO
Isso é inegociável. Se você perder sua estrutura de URL, você perde seus rankings de busca. Período.
Mapa de redirect
WordPress usa padrões de URL como /2024/03/post-slug/ ou /category/term/. Seu site Next.js provavelmente usa /blog/post-slug. Crie um mapa de redirect:
// next.config.js
module.exports = {
async redirects() {
return [
// URLs baseadas em data para slugs limpos
{
source: '/:year(\\d{4})/:month(\\d{2})/:slug',
destination: '/blog/:slug',
permanent: true,
},
// Arquivos de categoria
{
source: '/category/:slug',
destination: '/blog?category=:slug',
permanent: true,
},
// Paginação
{
source: '/page/:num',
destination: '/blog?page=:num',
permanent: true,
},
// URLs de feed
{
source: '/feed',
destination: '/rss.xml',
permanent: true,
},
];
},
};
Para sites grandes (1000+ redirects), use a configuração vercel.json do Vercel ou middleware ao invés — redirects em next.config.js têm um limite suave em torno de 1024 entradas antes dos tempos de build começarem a sofrer.
Dados estruturados
Plugins do WordPress como Yoast geram JSON-LD automaticamente. Você precisa replicar isso:
// 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: 'Nome do seu site',
logo: { '@type': 'ImageObject', url: '/logo.png' },
},
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
Sitemap XML
Next.js 15 torna sitemaps dinâmicos diretos:
// 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://seusite.com', lastModified: new Date() },
...pages.map((page) => ({
url: `https://seusite.com/${page.slug}`,
lastModified: page.modified,
})),
...posts.map((post) => ({
url: `https://seusite.com/blog/${post.slug}`,
lastModified: post.modified,
})),
];
}
Deployment no Vercel: Configuração que realmente funciona
Configuração de projeto
npx create-next-app@latest my-migrated-site --typescript --tailwind --app
cd my-migrated-site
vercel link
Variáveis de ambiente
Defina estas no dashboard do Vercel, não em arquivos .env commitados ao Git:
CMS_API_URL=https://seu-endpoint-api-cms
CMS_API_TOKEN=seu-token-somente-leitura
REVALIDATION_SECRET=uma-string-aleatória-para-autenticacao-webhook
SITE_URL=https://seusite.com
Configuração Vercel
// vercel.json
{
"crons": [
{
"path": "/api/revalidate-sitemap",
"schedule": "0 */6 * * *"
}
],
"headers": [
{
"source": "/fonts/(.*)",
"headers": [
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
]
}
]
}
Webhook de revalidação on-demand
// 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'); // Revalidar listagem também
} else {
revalidatePath(`/${slug}`);
}
return NextResponse.json({ revalidated: true });
}
Aponte seu webhook CMS para https://seusite.com/api/revalidate com o header secreto. Agora atualizações de conteúdo aparecem em segundos sem rebuilds completos.
Benchmarks de performance: Antes e depois
Esses são números reais de migrações que completamos na Social Animal em 2025-2026:
| Métrica | WordPress (WP Engine) | Next.js (Vercel) | Melhoria |
|---|---|---|---|
| Lighthouse Performance | 62-78 | 95-100 | +30-40% |
| Largest Contentful Paint | 2.8-4.2s | 0.8-1.4s | 60-70% mais rápido |
| Time to First Byte | 800ms-1.5s | 50-120ms | 90%+ mais rápido |
| Cumulative Layout Shift | 0.12-0.25 | 0.01-0.05 | ~80% redução |
| Custo de hospedagem mensal | $115/mês média | $20-40/mês | 60-80% economia |
| Tempo de build (500 páginas) | N/A (dinâmico) | 45-90 segundos | N/A |
| Páginas/segundo (ISR) | 15-30 req/s | 10,000+ da edge | Ordens de magnitude |
A melhoria de TTFB sozinha vale a migração. WordPress gera cada página através de PHP e MySQL. Vercel entrega páginas pré-renderizadas de nós de edge em 300+ locais mundialmente.
Armadilhas comuns de migração
Armadilha 1: Esquecer feeds RSS do WordPress. Se pessoas se inscrevem no seu feed, redirecione /feed/ e /rss/ para um novo endpoint RSS. Next.js não gera feeds por padrão — você precisa de uma rota customizada.
Armadilha 2: Perder shortcodes do WordPress. Conteúdo exportado do WordPress está repleto de [gallery], [embed] e shortcodes específicos de plugin. Se você não parsear e converter esses, eles renderizam como texto simples. Escreva transformers para cada tipo de shortcode que seu conteúdo usa.
Armadilha 3: Ignorar dados de comentários do WordPress. Se você tem threads de comentários valiosos, migre-os para um serviço como Disqus ou construa um sistema de comentários customizado. A maioria das pessoas apenas abandona comentários, mas verifique com stakeholders primeiro.
Armadilha 4: Não testar links internos. Conteúdo WordPress está cheio de links internos usando URLs absolutas (https://seusite.com/old-path/). Esses precisam ser atualizados para caminhos relativos ou sua nova estrutura de URL. Uma simples regex find-and-replace durante migração manipula a maioria dos casos.
Armadilha 5: Esquecer referências wp-content/uploads. Mesmo após migrar imagens, conteúdo antigo pode referenciar caminhos /wp-content/uploads/2024/03/image.jpg. Configure um redirect catch-all ou proxy esses para sua nova CDN de imagens.
Se isso parece avassalador, isso é honestamente normal. Uma migração apropriada leva 4-12 semanas dependendo da complexidade do site. Confira nossos preços ou entre em contato direto se você quer mãos experientes no projeto.
FAQ
Quanto tempo leva uma migração de WordPress para Next.js? Para um site com 100-500 páginas, espere 4-8 semanas com um desenvolvedor dedicado. Sites maiores com custom post types, e-commerce ou conteúdo multilíngue podem levar 8-16 semanas. A migração de conteúdo em si é geralmente 20% do trabalho — os outros 80% são reconstruir templates, manipular edge cases e testes de QA em cada URL.
Vou perder meus rankings do Google durante migração? Não se manipular redirects apropriadamente. Implemente 301 redirects para cada URL que muda, preserve seus títulos meta e descrições, submeta seu novo sitemap ao Google Search Console e use a ferramenta Change of Address se seu domínio mudar. Espere uma pequena flutuação de ranking por 2-4 semanas, depois recuperação ou melhoria conforme Google reconhece melhor Core Web Vitals.
Posso manter usando WordPress como meu CMS com Next.js? Absolutamente. Isso é chamado "WordPress headless" e é uma abordagem popular. Use WPGraphQL para expor seu conteúdo como uma API, então o consuma do Next.js. Seus editores mantêm o admin do WordPress que conhecem. A principal desvantagem é que você ainda mantém uma instalação WordPress — atualizações de segurança, hospedagem, toda stack.
Quanto custa migrar do WordPress para Next.js? DIY com um desenvolvedor: 100-300 horas de trabalho. Migração de agência: tipicamente $15.000-$75.000 dependendo da complexidade. Custos de hospedagem contínuos geralmente diminuem — Vercel Pro a $20/usuário/mês versus hospedagem WordPress gerenciada a $50-$300/mês. O ROI vem de custos de hospedagem reduzidos, melhor performance (que melhora taxas de conversão) e menor overhead de manutenção.
Devo usar Pages Router ou App Router em Next.js 15? App Router, sem dúvida. Em 2026, o App Router é o padrão estável com Server Components, streaming e rotas paralelas. O Pages Router ainda funciona e não está deprecado, mas novos features e otimizações são App Router-first. Toda migração que fazemos na Social Animal usa o App Router exclusivamente. Confira nossas capacidades de desenvolvimento Next.js para mais sobre nossa abordagem.
E quanto a formulários e funcionalidade dinâmica? Rotas API do Next.js (ou Server Actions no App Router) manipulam submissões de formulário, busca, autenticação e qualquer lógica server-side. Para formulários de contato, você pode usar Server Actions com um serviço como Resend para entrega de email. Para busca, considere Algolia ou Meilisearch. Para autenticação, NextAuth.js (agora Auth.js v5) cobre a maioria dos casos de uso.
Vercel é a única opção para hospedar Next.js? Não. Você pode fazer deployment de Next.js no Netlify, AWS Amplify, Cloudflare Pages ou auto-hospedar com Node.js. No entanto, Vercel é construído pelo time do Next.js, e a integração se mostra — ISR, edge middleware, otimização de imagem e analytics todos funcionam melhor no Vercel. A lacuna de DX entre Vercel e alternativas reduziu em 2026, mas Vercel continua o caminho de menor resistência.
E se eu precisar migrar uma loja WooCommerce? Este é um projeto maior. A maioria dos times migra a storefront para Next.js enquanto move o backend de comércio para Shopify (usando Storefront API), Medusa.js ou Saleor. Framework Hydrogen do Shopify é outra opção, mas se você quer controle total sobre o frontend, Next.js com a API do Shopify é o caminho mais provado. Espere a migração de e-commerce adicionar 4-8 semanas à sua timeline.