SEO Checklist for Migrating from WordPress (Don't Lose Rankings)
Asad Ali
Founder & Lead Developer · Former WordPress Core Contributor
SEO Checklist for Migrating from WordPress (Don't Lose Rankings)
Migrating from WordPress can dramatically improve performance, but done wrong, you could lose years of SEO value. This checklist ensures you preserve your rankings.
Pre-Migration Phase
1. Document Current State
Before touching anything:
Export current URLs:
Using Screaming Frog or similar
Export all URLs to spreadsheet
Document:
- [ ] All page URLs
- [ ] All blog post URLs
- [ ] Category/tag URLs
- [ ] Media URLs
- [ ] Current rankings for top keywords
- [ ] Current traffic levels (GA baseline)
Tools:
- Screaming Frog (crawl all URLs)
- Google Search Console (export performance data)
- Ahrefs/Semrush (export rankings)
2. Export SEO Settings
From Yoast, Rank Math, or similar:
- [ ] Meta titles
- [ ] Meta descriptions
- [ ] Focus keywords
- [ ] Canonical URLs
- [ ] Robots directives (noindex pages)
- [ ] Schema markup settings
Store in spreadsheet matched to URLs.
3. Map URL Structure
Compare old URLs to new:
| Old (WordPress) | New (Static Site) |
/blog/post-name/ | /blog/post-name |
/category/seo/ | /blog/category/seo |
/2026/01/post/ | /blog/post |
Identify:
- URLs that stay the same ✅
- URLs that change (need redirects) ⚠️
- URLs being removed (need redirect plan) ❌
URL Preservation & Redirects
4. Match URL Structure (When Possible)
Best case: keep URLs identical.
WordPress:
/blog/my-awesome-post/
New site:
/blog/my-awesome-post/
(Or /blog/my-awesome-post without trailing slash—pick one consistently)
5. Set Up 301 Redirects
For any URLs that change, create 301 (permanent) redirects.
In Next.js (next.config.js):
module.exports = {
async redirects() {
return [
{
source: '/old-url/',
destination: '/new-url',
permanent: true,
},
// Category example
{
source: '/category/:slug*',
destination: '/blog/category/:slug*',
permanent: true,
},
];
},
};
In Vercel (vercel.json):
{
"redirects": [
{ "source": "/old-url/", "destination": "/new-url", "permanent": true }
]
}
In Netlify (_redirects):
/old-url/ /new-url 301
/category/* /blog/category/:splat 301
6. Handle Common WordPress URLs
Redirect these automatically:
// WordPress patterns that need redirects
const redirects = [
// Paginated archives
{ source: '/page/:num', destination: '/blog' },
// Author archives
{ source: '/author/:slug', destination: '/about' },
// Date archives (if not preserving)
{ source: '/:year/:month/:slug', destination: '/blog/:slug' },
// Feed URLs
{ source: '/feed/', destination: '/rss.xml' },
// Search
{ source: '/\\?s=:query', destination: '/search?q=:query' },
];
7. Verify All Redirects Work
After setup, test every redirect:
Using curl
curl -I https://newsite.com/old-url/
Should show: 301 Moved Permanently
Location: /new-url
Or use redirect checker tools.
Metadata Migration
8. Transfer Meta Titles
Every page needs its meta title preserved.
In Next.js (page.tsx):
export const metadata = {
title: 'Your Preserved Meta Title',
description: 'Your preserved meta description',
};
For blog posts (MDX frontmatter):
---
title: "Post Title"
seo:
title: "Your SEO Title | Site Name"
description: "Your meta description here"
9. Transfer Meta Descriptions
Every indexed page needs its description.
Checklist:
- [ ] Homepage meta description
- [ ] All blog posts
- [ ] All pages
- [ ] Category pages
- [ ] Any custom post types
10. Preserve Open Graph Tags
For social sharing:
// Next.js metadata
export const metadata = {
openGraph: {
title: 'Page Title',
description: 'Description',
images: ['/og-image.jpg'],
type: 'article',
publishedTime: '2026-02-05',
},
twitter: {
card: 'summary_large_image',
title: 'Page Title',
description: 'Description',
images: ['/og-image.jpg'],
},
};
Technical SEO
11. Create XML Sitemap
Next.js (app/sitemap.ts):
import { getAllPosts } from '@/lib/posts';
export default async function sitemap() {
const posts = await getAllPosts();
const postUrls = posts.map((post) => ({
url: https://yoursite.com/blog/${post.slug},
lastModified: post.updatedAt || post.publishedAt,
changeFrequency: 'weekly',
priority: 0.8,
}));
return [
{
url: 'https://yoursite.com',
lastModified: new Date(),
changeFrequency: 'daily',
priority: 1,
},
...postUrls,
];
}
12. Set Up Robots.txt
// app/robots.ts
export default function robots() {
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: ['/api/', '/admin/'],
},
],
sitemap: 'https://yoursite.com/sitemap.xml',
};
}
13. Implement Canonical URLs
Every page needs a canonical URL:
export const metadata = {
alternates: {
canonical: 'https://yoursite.com/this-page',
},
};
14. Preserve Structured Data (Schema)
Transfer your schema.org markup:
// BlogPosting schema
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.title,
description: post.excerpt,
image: post.featuredImage,
datePublished: post.publishedAt,
dateModified: post.updatedAt,
author: {
'@type': 'Person',
name: post.author.name,
url: 'https://yoursite.com/about',
},
publisher: {
'@type': 'Organization',
name: 'Your Site',
logo: {
'@type': 'ImageObject',
url: 'https://yoursite.com/logo.png',
},
},
};
return (
<>