Direct answer
A UK business can move from WordPress to Next.js without losing search visibility if it treats the move as a URL migration, not a redesign. Before launch, list every URL that Google, customers or other websites still use, from Search Console, a crawl, the old sitemaps and backlink data. Give each one a single disposition: keep it at the same path, redirect it in one permanent hop to a page with the same intent, or retire it with a 404 or 410. Then prove content, metadata, structured data and internal-link parity on staging, switch over and watch errors and Search Console daily. Google says permanent redirects do not lose PageRank, but it still takes time for Google to recrawl and process the change. Google: site moves with URL changes
Executive summary
- The framework does not rank; preserved URLs, content and signals do. Next.js changes how you deliver them.
- Build the inventory from Search Console, logs and backlinks as well as a crawl, then score each URL for equity and intent.
- Redirect only to same-intent pages, in a single 301 or 308 hop; retire the rest with a 404 or 410.
- Keep platform and domain changes separate where you can, and keep redirects for at least a year, preferably indefinitely.
What our own WordPress-to-Next.js move taught us
Between August and October 2026, Appycodes rebuilt and consolidated its own website estate in Next.js. The appycodes.com domain had previously hosted a WordPress site, and Google still held some of its URLs. The new build launched on appycodes.dev, became the canonical global site on appycodes.com on 23 September 2026, and split UK-specific pages onto appycodes.co.uk. Every step is recorded in the site's repository, so we can describe what happened rather than what we intended.
The difficult decision came first. The previous site had 35 narrow service URLs. The new information architecture had six service pillars, and the clean option was to 301 all 35 into those pillars. We did not. The rebuild carried the 35 pages over at their existing URLs as real pages, each with its own content, recovered from archived copies of the old pages where they existed and rewritten where they did not. The repository commit gives the reason in one line: so each URL keeps its own indexable content. The cost was real: more pages to write, review and maintain, and some of them thinner than a pillar page. Eleven historical service slugs that no longer matched a distinct service were redirected to their current service page instead, using explicit 301s.
The audit work was also concrete. Before the domain switch, the team produced a page-by-page review table recording each URL's Google indexing state, HTTP status, 90-day clicks and impressions against the previous 90 days, internal links pointing at it, and a P0 to P2 priority. That baseline is what a migration is measured against later.
The first domain catch-all sent /about on the old host to /about on the new one, and the trailing-slash rule added a second redirect.
A rule keeping the UK domain to UK content sent a newly UK-canonical offer page back to .com, while .com sent it to .co.uk: a redirect loop.
The exclusion list is now derived from the list of UK-canonical paths, so the two rules cannot disagree.The post-migration audit found that the legacy WordPress /contact-us URL was still the domain's most-shown page in Google results, and it no longer resolved.
Google had indexed .htm pages injected into the old WordPress install, under random paths no legitimate page used.
The pattern in those four incidents is the reason for this guide. None was a framework problem. Each was a URL or response that no single owner had tested end to end: a hop created by two correct rules, two hosts with rules that disagreed, a URL only Google remembered, and junk the old platform left behind.
The Appycodes URL Equity Ledger
The ledger is a spreadsheet with one row per legacy URL and one disposition per row. It works for a 60-page professional-services site and for a 6,000-page publisher; only the thresholds change. Score three equity signals and one intent signal from 0 to 3, then apply one hygiene gate.
0 no impressions in Search Console's 16 months · 1 impressions only · 2 recent clicks · 3 a top landing page.
0 no external referring domains · 1 one · 2 a few · 3 many, or any from a trade body, council, university or press.
0 no role · 1 assists · 2 lands enquiries · 3 contact, quote, booking, order or a legal page.
Best new page: 0 none · 1 broad parent topic · 2 same intent, new format · 3 same content.
Fail for injected spam, hacked paths, search-result pages and junk parameters. A fail always retires.
Equity of 6 or more makes a URL P0: it is checked on staging, in the first hour after cutover and again in the first week. The thresholds above are a decision aid, not a statistical model. Calibrate "recent clicks" and "many links" to the size of the site, and record the reason beside every judgement so the ledger survives handover.
The rule that matters most is the third route. Google advises against redirecting many old URLs to an irrelevant single destination such as the homepage, and it recommends server-side permanent redirects such as 301 or 308 that go straight to the final destination. Google: redirects and Google Search
Decision table: what to do with WordPress URL types
WordPress generates more public URLs than most owners realise. The table covers the URL types we meet on almost every UK WordPress migration. Score each one in the ledger; these are the typical outcomes, not fixed answers.
| WordPress URL type | Typical disposition | Watch for |
|---|---|---|
| Pages and posts with demand or links | Keep at the same path; content and metadata carried across | Changed slugs, dates added to post paths, lost H1s or FAQ content in a redesign |
| Merged or renamed services | Redirect to the same-intent page, or rebuild in place if equity is high | Redirecting a specific service to a generic pillar that no longer answers the query |
| Category and tag archives | Keep the few with demand as curated hub pages; retire thin ones | Hundreds of tag URLs redirected to the blog index |
| Author, date and attachment pages | Retire unless they have links or demand; redirect attachments to their parent page or the file | Attachment pages that ranked in image search |
Media under /wp-content/uploads/ | Keep the paths or redirect each file to its new URL | PDFs, price lists and images linked from other websites and emails |
/?p=123, /feed/, /wp-sitemap.xml, SEO-plugin sitemaps | Redirect known query and feed URLs with query-aware rules; publish the new sitemap and submit it | Path-only redirect rules cannot match a query string |
| Contact, quote, booking and legal pages | Keep or redirect, always P0 | Old URLs printed on vans, invoices, proposals and email signatures |
| Injected spam or hacked paths | Retire as a class with a 410 | A catch-all route or soft 404 answering them with a 200 |
Prove parity on staging before the DNS switch
Redirects protect the URLs that change. Parity protects the URLs that stay. A Next.js rebuild can keep every path and still lose search signals if templates drop content, metadata or structured data. Use one acceptance record per P0 URL.
| Check | Pass condition | UK-specific note |
|---|---|---|
| Response | 200 for keep URLs; one hop then 200 for redirects; 404 or 410 for retired URLs | Test the www and non-www hosts, HTTP and HTTPS, and any .com or .co.uk variant |
| Canonical and hreflang | Self-referencing canonical on the production host; hreflang only between real equivalents | A business with .com and .co.uk editions needs a deliberate en-GB and x-default plan |
| Titles, descriptions, H1 | Carried across or deliberately improved, never blank or templated duplicates | Location wording such as town or county names often lives only in old titles |
| Body content and FAQs | Substantive content present in the server-rendered HTML | Accreditations, regulator numbers and service-area copy are easy to cut in a redesign |
| Structured data | Organisation, breadcrumb, article, product or FAQ markup equivalent or better | Keep the registered business name and address consistent with other listings |
| Internal links and sitemap | Internal links point at final URLs; sitemap lists only canonical 200 URLs | Update links in Google Business Profile, directories and email templates you control |
| Trading disclosures | Footer or legal page shows the required company details | A UK limited company must show its registered name, number, registered office and part of the UK where registered on its website. GOV.UK: company trading disclosures |
| Forms, consent and analytics | Enquiries reach the inbox or CRM; consent blocks non-essential tags until accepted | Rebuilt forms change where personal data goes; update the privacy notice and cookie set-up |
| Robots and noindex | Staging blocked; production free of staging noindex headers and disallow rules | Check response headers as well as meta tags; our own noindex rules are header-based |
If the move also changes domain, Google's Change of Address tool is used after the redirects are live, works at domain level rather than for paths, and needs the same Google account to own both Search Console properties. Search Console Help: Change of Address tool A same-domain platform change does not use it; you submit the new sitemap and monitor the existing property.
Implementation: compile the ledger, retire spam, test every hop
Next.js applies redirects() from next.config.ts before serving pages. Marking a rule permanent: true returns a 308; you can set statusCode: 301 instead. Google treats both as permanent. Next.js: redirects configuration We use explicit 301s across our own estate for consistency with older tools and audit expectations, not because Google needs them.
Do not hand-type a long redirect list. Export the ledger to JSON, compile it, and fail the build when a rule chains, points at a page that does not exist or dumps URLs on the homepage. The script below is the shape we use; adapt the file paths and thresholds to your project.
// scripts/compile-url-ledger.ts
// Turns the reviewed migration ledger into Next.js redirects and fails the
// build on chains, homepage dumping and high-equity URLs with no home.
import { readFileSync, writeFileSync } from "node:fs";
type Level = 0 | 1 | 2 | 3;
type Row = {
path: string; // legacy path, e.g. "/contact-us/"
demand: Level; // Search Console clicks and impressions
links: Level; // external referring domains
conversion: Level; // enquiry, booking, order or legal role
intent: Level; // match of the best page on the new site
hygiene: "pass" | "fail"; // "fail" = injected, spam or junk parameter URL
target?: string; // best new page when intent >= 2
keepInPlace?: boolean; // the new site serves this exact path
};
type Disposition = "keep" | "redirect" | "retire";
const withSlash = (p: string) =>
/\.[a-z0-9]+$/i.test(p) || p.endsWith("/") ? p : `${p}/`;
export function dispose(row: Row): Disposition {
const equity = row.demand + row.links + row.conversion;
if (row.hygiene === "fail") return "retire";
if (row.keepInPlace) return "keep";
if (row.intent >= 2 && row.target) return "redirect";
if (equity >= 4) {
throw new Error(`${row.path}: equity ${equity}, rebuild in place or map a target`);
}
return "retire";
}
const ledger: Row[] = JSON.parse(readFileSync("migration/url-ledger.json", "utf8"));
// Every path the new Next.js build actually serves with a 200.
const liveRoutes = new Set<string>(
JSON.parse(readFileSync("migration/new-routes.json", "utf8")),
);
const legacySources = new Set(ledger.map((row) => withSlash(row.path)));
const redirects: { source: string; destination: string; statusCode: 301 }[] = [];
const errors: string[] = [];
let toHomepage = 0;
for (const row of ledger) {
const source = withSlash(row.path);
let disposition: Disposition;
try {
disposition = dispose(row);
} catch (error) {
errors.push((error as Error).message);
continue;
}
if (disposition === "keep" && !liveRoutes.has(source)) {
errors.push(`${source}: marked keep but the new site does not serve it`);
}
if (disposition !== "redirect") continue;
const destination = withSlash(row.target!);
if (destination === source) errors.push(`${source}: redirects to itself`);
if (legacySources.has(destination) && !liveRoutes.has(destination)) {
errors.push(`${source}: chains through another legacy URL ${destination}`);
}
if (!liveRoutes.has(destination)) errors.push(`${source}: ${destination} is not live`);
if (destination === "/") toHomepage += 1;
// With trailingSlash: true, register both spellings so each is one hop.
if (source.endsWith("/") && source !== "/") {
redirects.push({ source: source.slice(0, -1), destination, statusCode: 301 });
}
redirects.push({ source, destination, statusCode: 301 });
}
if (toHomepage > 3) errors.push(`${toHomepage} URLs redirect to the homepage`);
if (redirects.length > 1000) errors.push("Move this map into a proxy lookup");
if (errors.length) {
console.error(errors.join("\n"));
process.exit(1);
}
writeFileSync("migration/redirects.generated.json", JSON.stringify(redirects, null, 2));
console.log(`${redirects.length} redirect rules written`);The generated file then feeds the config, and a small proxy rule retires a whole class of injected URLs. The .htm rule is the one we run on appycodes.com; it is safe there because the new site never serves a .htm path. Check your own legacy URLs before copying the pattern.
// next.config.ts
import type { NextConfig } from "next";
import legacy from "./migration/redirects.generated.json";
const nextConfig: NextConfig = {
trailingSlash: true,
async redirects() {
return legacy as { source: string; destination: string; statusCode: 301 }[];
},
};
export default nextConfig;
// proxy.ts (Next.js 16; middleware.ts in earlier versions)
// Retire a whole class of injected WordPress spam URLs with a 410.
import { NextResponse, type NextRequest } from "next/server";
export function proxy(request: NextRequest) {
if (request.nextUrl.pathname.endsWith(".htm")) {
return new NextResponse("410 Gone", {
status: 410,
headers: { "X-Robots-Tag": "noindex" },
});
}
return NextResponse.next();
}
export const config = { matcher: ["/((?!_next/|api/).*)\\.htm"] };Next.js recommends moving beyond the config file once a site needs more than about 1,000 redirects, and notes a limit of 1,024 on Vercel; its guide shows a proxy-based lookup instead. Next.js: redirecting at scale Query-string URLs such as WordPress ?p= links also need query-aware rules or a proxy lookup, because a path pattern alone will not match them.
Before and after the switch, run the P0 list against the real hosts. Googlebot follows up to ten redirect hops, but Google advises pointing straight at the final destination, so the check treats anything above one hop as a failure. Google: how HTTP status codes affect crawlers
# p0-urls.txt: every Keep and Redirect URL with demand, links or enquiries.
# Expect 200 with 0 hops (keep) or 200 after exactly 1 hop (redirect).
while read -r url; do
curl -s -o /dev/null -L --max-redirs 5 \
-w "%{http_code}\t%{num_redirects}\t$url -> %{url_effective}\n" "$url"
done < p0-urls.txt |
awk -F'\t' '$1 != 200 || $2 > 1 { print "FAIL\t" $0; next } { print "ok\t" $0 }'- Freeze. Stop content publishing on WordPress a few days before cutover, or replay new posts into the ledger and the new site.
- Prove. Run the compiler, the parity record and the hop check on staging with production hostnames mapped locally.
- Switch. Change DNS or the origin in a low-traffic window, with the WordPress database and files backed up and the old host kept recoverable.
- Submit. Publish and submit the new sitemap; for a domain change, run Change of Address after redirects are live.
- Watch. Review 4xx and 5xx logs daily for two weeks. Any legacy URL that appears with real traffic goes back into the ledger.
Seven failure modes we design against
1. The inventory is the new sitemap
A sitemap lists what you plan to publish. Google, customers and other websites remember what you used to publish. Pull the full page list from Search Console, server logs and a backlink tool, and union it with a crawl of the old site. Our /contact-us 404 is exactly this failure.
2. Platform, domain and slash rules stack into chains
Old host to new host, old slug to new slug, no slash to slash: each rule is correct and together they make three hops. Compile rules to final destinations and test the combinations, as we had to on cutover day.
3. Everything without a match goes to the homepage
It looks tidy in a crawl report and helps nobody. Google may treat it as a soft 404, and users land somewhere irrelevant. Retire honestly and invest in a useful not-found page with search and the main routes.
4. Legacy junk is answered with a 200
A catch-all route, a generous dynamic segment or a branded not-found page served with a 200 makes injected or junk URLs look valid. Google's documentation treats 4xx codes other than 429 alike for indexing, so the precise choice of 404 or 410 matters less than never answering with a 200. We use 410 for spam classes because it is the clearest statement of intent.
5. A non-SEO change breaks every URL at once
On our cutover day, a favicon file placed where Next.js parses icons as route metadata made every page return a 500 in testing. It was caught before it was committed and the file moved to the public folder, but it had nothing to do with search and would have turned the whole site into server errors. Run a full-site smoke test after every cutover-day change, not only after redirect changes.
6. Staging protection reaches production
Noindex headers, robots disallow rules and password protection belong to staging. Scope them by hostname or environment and check response headers on the production host immediately after the switch.
7. Media and documents are forgotten
UK B2B sites often have brochures, price lists, case-study PDFs and safety data sheets linked from emails and partner sites. If /wp-content/uploads/ paths disappear, those links break quietly. Keep the paths or map each file.
Recommendations for UK businesses
Most of the value sits in a small set of service, location and contact URLs. Score them P0, keep their paths where possible and test forms end to end. A 60 to 200-page site can be ledgered by hand in a day or two.
Years of news, reports and event pages carry links from councils, universities and partners. Keep linked content at the same path, turn valuable categories into hubs and retire the thin remainder with 404s.
Moving the storefront is a different project from moving pages. Consider a Next.js front end over the existing WordPress and WooCommerce back end first, as in our All White Laser work, before replatforming orders and payments.
Choose which host is canonical for each page set, add hreflang only between true equivalents and avoid serving the same page on both. We split our own estate this way; it adds rules, so test the redirect matrix across every host.
After our own migration and client rebuilds, Appycodes recommends four things. Change the platform and the domain in separate steps when the business can wait; we combined a domain move with a UK host split and paid for it with a two-hop redirect and a redirect loop in the first three days. Build the ledger from Google's data, not from the new site map. Keep redirects for every URL with links, enquiries or demand indefinitely; Google's minimum is about a year. And budget for the two weeks after launch, because that is where the URLs nobody listed show up.
Our performance and search service covers the ledger, redirect compilation, parity testing, cutover and post-launch monitoring for UK businesses moving to Next.js. The All White Laser case study shows a Next.js front end built over an existing WordPress and WooCommerce back end for a UK business; it is related delivery evidence, not a published search-traffic outcome.
Frequently asked questions
- Will moving from WordPress to Next.js improve my Google rankings?
- Not by itself. Google ranks the content and signals it can crawl, not the framework. A well-built Next.js site can be faster and easier to keep tidy, but the migration's first job is to preserve what already works: URLs, content, metadata, structured data and internal links. Any ranking gain should be treated as a possible later benefit, not a migration promise.
- Should I use 301 or 308 redirects in Next.js?
- Either is a permanent redirect for Google Search. Next.js uses 308 when a redirect is marked permanent: true, and accepts statusCode: 301 when you want a 301 instead. Choose one convention, use it consistently and make sure every legacy URL reaches its final destination in a single hop.
- How long should I keep redirects after a website migration?
- Google's site-move guidance says to keep redirects for as long as possible and generally for at least a year. Appycodes keeps redirects indefinitely for any legacy URL with external links, enquiries or Search Console demand, because the maintenance cost of a redirect rule is far lower than the cost of breaking a link someone still uses.
- Should old WordPress pages with no replacement redirect to the homepage?
- No. Google advises against redirecting many old URLs to one irrelevant destination such as the homepage, and it may treat that pattern like a missing page. Redirect a URL only when a page with the same intent exists. Otherwise return a 404 or 410 and make the new site's navigation and search good enough for a human to recover.
- Is a 410 better than a 404 for removing old URLs from Google?
- Google's current documentation treats 4xx responses other than 429 alike: the URL is considered not to exist and previously indexed URLs are removed. A 410 is still a clear, honest answer for a class of URLs that will never return, such as injected spam pages. The important part is to avoid answering them with a 200 page or a blanket redirect.
Primary sources
- Google Search Central: site moves with URL changes
- Google Search Central: redirects and Google Search
- Google: how HTTP status codes affect Google's crawlers
- Search Console Help: Change of Address tool
- Next.js: redirects configuration
- Next.js: redirecting and redirects at scale
- GOV.UK: company details on websites and stationery
Technical and operational guidance, not legal advice. Search engines process migrations on their own timetable and no migration method guarantees rankings. Confirm company-disclosure and data-protection obligations for your business with a qualified adviser.
UK topic cluster
Engineering & compliance
UK guidance on hosting, GDPR, performance, migrations and agency delivery.
Related guide
Taking over a website from another UK agency
Secure domains, DNS, code, hosting, data and search access before you change anything.
Architecture
WordPress to headless Next.js
When to keep WordPress as the CMS behind a faster Next.js front end.
Case study
All White Laser
A Next.js front end over WordPress and WooCommerce, with billing and support systems behind it.










































