NNicheStackTutorials
← All tutorials
Static Hosting

Vercel Redirects After WordPress → Hugo Migration (94% Link Recovery)

WordPress migration broke old URLs. vercel.json regex redirects preserved 94% of inbound links per Google Search Console.

·12 min read·Static Hosting
Vercel Redirects After WordPress → Hugo Migration (94% Link Recovery) cover

Migration: /?p=123 WordPress → /posts/slug/ Hugo on Vercel Week 1: 404 errors in GSC jumped to 1,200/day

TL;DR

  • Map old URL patterns to new paths with vercel.json redirects.
  • Use regex captures for date-based WordPress permalinks.
  • Verify in GSC Links report after deploy.

Mapping file

Exported WordPress redirects CSV → generated JSON:

{
  "redirects": [
    { "source": "/hello-world/", "destination": "/posts/hello-world/", "permanent": true },
    { "source": "/category/javascript/", "destination": "/tags/javascript/", "permanent": true }
  ]
}

200 entries — under Vercel limit but unwieldy.

Regex for date-based URLs

Old pattern: /2024/03/my-post/ → new: /posts/my-post/

{
  "redirects": [
    {
      "source": "/:year(\\d{4})/:month(\\d{2})/:slug",
      "destination": "/posts/:slug",
      "permanent": true
    }
  ]
}

Note double backslashes in JSON for \d.

Rewrite for client-side SPA (not my case)

Don't confuse redirect with rewrite:

{ "source": "/app/:path*", "destination": "/app/index.html" }

That's for SPAs. Blogs want 308 permanent redirects for SEO.

Trailing slash trap

/posts/hello → 404
/posts/hello/ → 200

Hugo uses trailing slashes. Add:

{ "source": "/posts/:slug", "destination": "/posts/:slug/", "permanent": true }

Test before deploy

npx vercel dev
# curl -I http://localhost:3000/2024/03/old-post
# HTTP/1.1 308 Permanent Redirect
# Location: /posts/old-post/

GSC result after 3 weeks

404 count dropped 1,200 → 80/day. Remaining were external bad links, not migration.

Bulk generate redirects from CSV

// scripts/wp-to-vercel.mjs
import fs from 'node:fs';
const rows = fs.readFileSync('redirects.csv', 'utf8').trim().split('\n').slice(1);
const redirects = rows.map(line => {
  const [from, to] = line.split(',');
  return { source: from.trim(), destination: to.trim(), permanent: true };
});
fs.writeFileSync('vercel.json', JSON.stringify({ redirects }, null, 2));

Run once during migration — don't hand-edit 200 rules.

Limit

Vercel supports 1,024 redirects per project on Hobby. Split across vercel.json + _redirects if needed.

FAQ

How do I redirect old WordPress URLs on Vercel?

Use vercel.json redirects array with source patterns matching old permalinks and destination new paths.

Do Vercel redirects preserve SEO?

301 redirects pass most link equity. Monitor GSC for 404 spikes after migration.

Can I use regex in Vercel redirects?

Yes — source supports regex with capture groups: /blog/(\d{4})/:slug → /posts/:slug.

Tags: VercelRedirectsSEOMigration