Handling Nuxt Route Changes During Site Migration

Problem Statement

You are restructuring routes in a Nuxt 3 application during a migration and need old paths to 301 to their new locations without a client-side flash or a broken crawl. Nuxt 3 runs on the Nitro server engine, which means redirects can be declared statically as routeRules in nuxt.config or computed dynamically in Nitro server middleware. This page is part of CMS & Framework Routing Changes and covers both paths.

Nuxt redirect path through Nitro Request enters Nitro, which applies server middleware first then routeRules, issuing a server-side 301 before the page component renders. Nuxt Redirect via Nitro (Server-Side) Request server middleware routeRules 301 old host/path host / locale path rename new URL Redirect issued before the page component renders — no client flash
Nitro applies server middleware then routeRules, emitting a true server-side redirect.

When to Use This Approach

  • Your front end is Nuxt 3 (or Nuxt Bridge) and you control nuxt.config.
  • You are renaming route segments or collapsing route groups during the migration.
  • You want server-rendered 301s, not client-side navigateTo redirects that flash a page first.
  • Some redirects must read the incoming request (host, header, locale) and so need Nitro middleware.
  • You are moving the Nuxt app to a new domain alongside the route changes.

Step-by-Step Instructions

1. Declare Static Redirects with routeRules

routeRules in nuxt.config are applied by Nitro before the page component runs, so they emit a true server-side redirect. Use a redirect object with an explicit statusCode.

// nuxt.config.ts — Nitro applies these on the server, no component render
export default defineNuxtConfig({
  routeRules: {
    '/blog/**': { redirect: { to: '/articles/**', statusCode: 301 } },
    '/promo':   { redirect: { to: '/offers', statusCode: 302 } },
  },
});

2. Preserve Path Segments with Wildcards

The ** wildcard captures the remainder of the path so a single rule can cover an entire legacy section instead of one rule per URL.

// One rule migrates the whole /docs tree to /help
routeRules: {
  '/docs/**': { redirect: { to: '/help/**', statusCode: 301 } },
}

3. Handle Dynamic Cases with Nitro Server Middleware

When a redirect depends on the request — host during a multi-domain move, a header, or a locale cookie — use a Nitro server middleware file under server/middleware/. sendRedirect issues the response before rendering.

// server/middleware/redirect.ts — redirect requests on the old host
export default defineEventHandler((event) => {
  const host = getRequestHeader(event, 'host');
  if (host === 'old.example.com') {
    const url = getRequestURL(event);
    return sendRedirect(event, `https://www.example.com${url.pathname}${url.search}`, 301);
  }
});

4. Keep Query Strings Intact

sendRedirect does not append the query string for you — read it from the request URL and pass it through, as ${url.search} above. For routeRules, the wildcard target preserves the captured path but confirm parameters survive in testing.

// Append the original query string explicitly so trackers survive
const target = `/articles${url.pathname.replace('/blog', '')}${url.search}`;
return sendRedirect(event, target, 301);

Worked Example

A Nuxt 3 site moving /blog to /articles and changing host. With the routeRules wildcard plus the host middleware deployed:

$ curl -sIL https://old.example.com/blog/nuxt-migration?ref=newsletter
HTTP/1.1 301 Moved Permanently
location: https://www.example.com/articles/nuxt-migration?ref=newsletter
HTTP/1.1 200 OK

The request resolves in a single server-side 301, the ref parameter is preserved, and no client-side render flashes the old page first. Order matters: the host middleware runs first to fix the domain, and the route rename is handled by routeRules on the new host.

Verification

  • Confirm a single server-side hop: curl -sIL https://old.example.com/blog/nuxt-migration | grep -iE '^HTTP|^location'.
  • Verify no client-side redirect by checking the status is 301 in the headers, not a 200 with a JS navigation.
  • Re-crawl the legacy route tree and confirm the query string survives on parameterised URLs.

FAQ

Should I use routeRules or server middleware for Nuxt redirects? Use routeRules for static, request-independent path changes — they are declarative and easy to audit. Use Nitro server middleware only when the redirect must read the request (host, header, locale), as in a multi-domain move.

Why not redirect in the page component with navigateTo? A component-level redirect runs after the page starts rendering, which can flash the old content and is not a clean server-side 301 for crawlers. Prefer routeRules or Nitro middleware so the redirect happens before render.

Related

← Back to CMS & Framework Routing Changes