Redirects and Gone URLs

When you move or delete a page, other sites and search results still link to the old URL. Use seo.redirects to list each old URL in one place, with the date you added it.

export default defineNuxtConfig({
  seo: {
    redirects: {
      '/blog/old-post': { to: '/blog/new-post', addedAt: '2026-10-13' },
      '/spring-sale': { statusCode: 410, addedAt: '2026-10-13', reason: 'The sale ended.' },
    },
  },
})

Learn when to redirect and when to return an error in the HTTP Redirects guide.

Moved URLs

An entry with to is a moved URL. A request for the old URL gets a permanent redirect.

  • The default status is 301.
  • If clients send a form POST to the old URL, set statusCode: 308. A 308 keeps the request method.
  • to is a path on your site, or an absolute URL on another site.
  • The redirect keeps the request query string. /blog/old-post?ref=mail redirects to /blog/new-post?ref=mail.

Google treats 301 and 308 the same. It uses both as a signal that the new URL is canonical.

Redirect only to a page that replaces the old one. If you redirect many deleted pages to the home page, Google might treat the redirects as soft 404 errors. If no page replaces the old one, use a gone URL.

Gone URLs

An entry with statusCode and no to is a gone URL. A request for the old URL gets your error page with that status.

  • If you deleted the content on purpose, use 410.
  • If you prefer the plain not found status, use 404.

Google treats 404 and 410 the same, and removes the URL from its index. A 410 also tells clients that the removal is likely permanent.

reason is a private note. The module never sends it to visitors.

Paths and Patterns

Write each key as a path without app.baseURL. The module adds the base URL to each path target.

A trailing /** matches the path and every path below it. To keep the rest of the path, end to with /**:

export default defineNuxtConfig({
  seo: {
    redirects: {
      // /docs/v1/setup redirects to /docs/setup
      '/docs/v1/**': { to: '/docs/**', addedAt: '2026-10-13' },
      // every path below /legacy redirects to one page
      '/legacy/**': { to: '/archive', addedAt: '2026-10-13' },
    },
  },
})
  • A trailing slash on a request does not change the match. /blog/old-post/ matches /blog/old-post.
  • An exact key wins over a /** key. A longer /** key wins over a shorter one.
  • Keys cannot contain a query string, a hash, :params, or a * in the middle.
  • A target can contain /** only at its end.

i18n

If the site uses an i18n prefix strategy, each unprefixed entry also applies under each locale prefix. With prefix_except_default, /old redirects to /new, and /fr/old redirects to /fr/new. Sites with differentDomains or multiDomainLocales get no copies.

If a locale uses a translated path, add an entry for it. An entry that starts with a locale prefix applies only to that locale.

export default defineNuxtConfig({
  seo: {
    redirects: {
      '/about-us': { to: '/about', addedAt: '2026-10-13' },
      '/fr/a-propos-de-nous': { to: '/fr/a-propos', addedAt: '2026-10-13' },
    },
  },
})

Review Instead of Expiry

No entry expires on its own. A redirect keeps working until you delete it.

Google advises that you keep redirects as long as possible, generally at least one year. For users, Google suggests that you keep them indefinitely. After you remove a redirect, new links to the old URL stop passing signals to the new URL.

So the module schedules a review instead of an expiry. If an entry is older than redirectReviewAfterDays (365 by default), nuxt dev and nuxt build list it. The age counts from the later of addedAt and reviewedAt.

[nuxt-seo-utils]  WARN  1 seo.redirects entry is due for review (older than 365 days):
  - /blog/old-post → /blog/new-post (since 2025-10-13, 365 days)
  If an old URL still gets visits or backlinks, keep it and set reviewedAt to today.
  If it gets none, you can delete the entry.

To review an entry, check the recent visits to the old URL in your server logs or analytics. Also check whether other sites still link to the old URL.

  • If the old URL still gets visits or links, keep the entry and set reviewedAt to today. The review clock restarts from that date.
  • If the old URL gets no visits and no links, you can delete the entry.
export default defineNuxtConfig({
  seo: {
    redirects: {
      '/blog/old-post': { to: '/blog/new-post', addedAt: '2025-10-13', reviewedAt: '2026-10-13' },
    },
  },
})

To change the window, set redirectReviewAfterDays. To turn off the review, set it to false.

Browser Caching

Chrome can cache a 301 or 308 redirect with no end date. So each redirect sends Cache-Control: public, max-age=3600. A later fix or removal then reaches returning visitors within an hour.

If a headers route rule sets Cache-Control for the same path, the module keeps that value.

Redirect Chains

Each hop in a chain adds a request. Google follows up to 10 hops. Google advises that you redirect straight to the final URL.

The module flattens chains in your entries. With /a to /b and /b to /c, a request for /a goes to /c in one hop. The build lists each chain that it flattened.

  • If entries form a loop, such as /a to /b and /b to /a, the build stops with an error.
  • If a moved URL redirects to a gone URL, the build warns. Pick a live target, or make the old URL a gone URL.
  • If an entry is not valid, the build stops with an error.
  • If addedAt or reviewedAt is not a valid date, the build warns. The entry still works, but the review skips it.

Long Lists

For a long list, keep the entries in their own file and import it in your Nuxt config. The Redirects type checks each entry.

redirects.ts
import type { Redirects } from 'nuxt-seo-utils'

export default {
  '/blog/old-post': { to: '/blog/new-post', addedAt: '2026-10-13' },
  '/spring-sale': { statusCode: 410, addedAt: '2026-10-13' },
} satisfies Redirects
nuxt.config.ts
import redirects from './redirects'

export default defineNuxtConfig({
  seo: { redirects },
})

Static Hosting

A static host has no Nuxt server, so it cannot run the redirects.

When you run nuxt generate, each exact moved URL becomes an HTML page with an instant meta refresh to the target. Google treats an instant meta refresh as a permanent redirect.

A static build cannot send a 410 status or match a /** key. The build lists these entries. If you need them on a static host, add rules to your host configuration. Otherwise the host returns its 404 page.

Client-Side Navigation

The redirects run on the server. A NuxtLink to an old URL behaves in one of two ways:

  • If no page matches the old URL, Nuxt loads the URL from the server. The redirect then applies.
  • If a catch-all page such as [...slug].vue matches the old URL, the app renders that page. The redirect does not apply.

Update your links to point at the new URL. Nuxt Link Checker flags links to a 301 redirect.

Other Modules

  • With Nuxt Sitemap, the module adds sitemap: false to each old URL. The sitemap then leaves it out, including its locale copies.
  • On a server build, the module adds prerender: false to each old URL. The prerender crawler then skips links to old URLs. Without this rule, it writes an HTML file that hides the redirect.

Route Rules

Nuxt routeRules also support redirect. Choose by the kind of redirect:

  • For old URLs that you moved or deleted, use seo.redirects. It adds dates, reviews, chain checks, and 410 support. It also adds app.baseURL to each path target.
  • For a temporary redirect, such as a 302 during maintenance, use routeRules redirect.

Verifying

Request the old URL and read the status and location header:

curl -sI https://example.com/blog/old-post | grep -iE '^(http|location)'
Was this page helpful?