---
title: "v5 to v6"
description: "Migrate from Nuxt SEO v5 to v6."
canonical_url: "https://nuxtseo.com/docs/nuxt-seo/migration-guide/v5-to-v6"
---

## Introduction

Nuxt SEO v6 moves every bundled module to a new major version. AI Ready and Skew Protection join the bundle. For the full list of changes, see the [v6 release notes](/docs/nuxt-seo/releases/v6).

::key-takeaways{title="Key takeaways"}
- First upgrade to Nuxt 4.6.1 or later, and Node 22.22.3+, 24.15.0+, or 26+. Nuxt 3 sites stay on Nuxt SEO v5.
- Remove `nuxt-ai-ready` and `nuxt-skew-protection` from `modules`. `@nuxtjs/seo` installs them.
- If OG Image signs URLs, set `NUXT_APP_SECRET` in production. Without it, production pages return a 500 error.
- Update or remove each bundled module that you pinned. An older pinned module now fails the build.
- Add `nuxtseo-devtools-host` as a development dependency to keep the SEO DevTools panels.
::

| Module                 | v5            | v6     |
| ---------------------- | ------------- | ------ |
| `nuxt-site-config`     | v4            | **v5** |
| `@nuxtjs/robots`       | v6            | **v7** |
| `@nuxtjs/sitemap`      | v8            | **v9** |
| `nuxt-og-image`        | v6            | **v7** |
| `nuxt-schema-org`      | v6            | **v7** |
| `nuxt-seo-utils`       | v8            | **v9** |
| `nuxt-link-checker`    | v5            | **v6** |
| `nuxt-ai-ready`        | *not bundled* | **v3** |
| `nuxt-skew-protection` | *not bundled* | **v2** |

::tip
Using an AI agent? The [release notes](/docs/nuxt-seo/releases/v6) include an upgrade prompt for your coding agent. Every module now ships an Agent Skill. Get the @nuxtjs/seo Skill on [skilld.dev/gh/harlan-zw/nuxt-seo](https://skilld.dev/gh/harlan-zw/nuxt-seo).
::

## Support

If you get stuck with the migration or have post-migration bugs, please get in touch.

- [Jump in the Discord](https://discord.com/invite/5jDAMswWwX)
- [Make a GitHub issue](https://github.com/harlan-zw/nuxt-seo/issues)

## Step 1: Update Nuxt and Node

### Nuxt 4.6.1 or later

Nuxt SEO v6 requires Nuxt `^4.6.1 || ^5.0.0`. Nuxt SEO v6 does not support Nuxt 3. If your site runs Nuxt 3, stay on Nuxt SEO v5.

Upgrade Nuxt before you upgrade Nuxt SEO:

```bash
npx nuxt upgrade
```

### Node 22.22.3+, 24.15.0+, or 26+

Node must match `^22.22.3 || ^24.15.0 || >=26.0.0`. Check `engines` in your `package.json`, your `.nvmrc`, and the Node versions in CI and in your deployment.

## Step 2: Update Nuxt SEO

```bash
npx nypm add @nuxtjs/seo@^6
```

### Remove AI Ready and Skew Protection from `modules`

`@nuxtjs/seo` now installs AI Ready and Skew Protection. Remove both from your `modules` array and from your dependencies. Keep your options under the `aiReady` and `skewProtection` keys.

```diff [nuxt.config.ts]
export default defineNuxtConfig({
  modules: [
    '@nuxtjs/seo',
-   'nuxt-ai-ready',
-   'nuxt-skew-protection',
  ],
})
```

To turn either module off, set its key to `false`:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  modules: ['@nuxtjs/seo'],
  aiReady: false,
  skewProtection: false,
})
```

If you keep both modules, your site gets new public output:

- **AI Ready** serves `/llms.txt` and a Markdown version of every page. A prerender also writes `/llms-full.txt`. AI Ready publishes Agent Skills from `public/skills/<name>/SKILL.md`. It generates an API catalog when you configure an MCP server and a site URL.
- **Skew Protection** keeps the assets of earlier builds after a deploy. Nuxt SEO sets `updateStrategy: 'polling'` and `cookie: false`, so it polls Nuxt's app manifest and sets no version cookie.

::warning
Skew Protection stores old assets in `node_modules/.cache/nuxt-seo/skew-protection` by default. If your CI starts from a clean checkout, cache that directory between builds, or set `skewProtection.storage`. Otherwise each deploy loses the assets of earlier builds.
::

If a server handler calls `isClientOutdated()`{lang="ts"}, set `skewProtection.cookie` to a cookie config object.

### If you already used Skew Protection

Skew Protection v2 changes these defaults:

- An unset `updateStrategy` now means hourly polling on every platform. For instant updates, set `updateStrategy: 'sse'` or `'ws'`.
- `experimental.appManifest: false` now fails setup.
- The default storage driver is `fs-lite`. An explicit `driver: 'fs'` still works.
- With `multiTab: true`, tabs share one push connection. To keep one connection per tab, set `multiTab: false` or `connectionTracking: true`.

### If you already used AI Ready

AI Ready v3 negotiates Markdown from the `Accept` header only. AI bots no longer get Markdown from their User-Agent alone. To restore that, set `botNegotiation`:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  aiReady: {
    botNegotiation: true,
  },
})
```

AI Ready v3 also drops the `better-sqlite3` fallback and uses `node:sqlite`.

### Update pinned module versions

If your `package.json` pins a bundled module, the pinned copy replaces the bundled one. An older pinned copy now fails the build and names the module:

```text
ERROR  [@nuxtjs/seo] Module `@nuxtjs/sitemap` version (`8.6.1`) does not satisfy `>=9.0.0` (requested by `@nuxtjs/seo`).
```

Update each pin to the version in the [table above](#introduction), or remove the pin and let `@nuxtjs/seo` install the module:

```diff [package.json]
{
  "dependencies": {
    "@nuxtjs/seo": "^6.0.0",
-   "@nuxtjs/sitemap": "^8.6.1"
+   "@nuxtjs/sitemap": "^9.0.0"
  }
}
```

If you cannot find the pin, run `pnpm why <package>`. If you use `@nuxtjs/i18n`, it must be v10 or later.

### Install the DevTools host

The SEO DevTools panels now live in an optional package. If you use them, add the host as a development dependency, then restart Nuxt:

```bash
npx nypm add -D nuxtseo-devtools-host
```

If the host is missing, each panel shows an install button.

### Check `nuxtseo: false`

In v5, `nuxtseo: false` type checked but installed every module anyway. In v6, it installs none of the bundled modules. If your config sets it, remove it to keep the modules. A module that you add to `modules` yourself still installs.

## Step 3: Set `NUXT_APP_SECRET` for OG Image

OG Image v7 signs image URLs with Nuxt's application secret. If URL signing is on, set `NUXT_APP_SECRET` to a value of at least 32 characters. Without it, production image and page requests return a 500 error.

Generate a secret:

```bash
npx nuxt-og-image generate-secret
```

Set it in your deployment environment, and in `.env` for local runs:

```bash [.env]
NUXT_APP_SECRET=<your-secret>
```

Remove the OG Image secret from your config:

```diff [nuxt.config.ts]
export default defineNuxtConfig({
  ogImage: {
-   security: {
-     secret: process.env.NUXT_OG_IMAGE_SECRET,
-   },
  },
})
```

`NUXT_OG_IMAGE_SECRET` still works with a deprecation warning. Signed URLs from OG Image v6 become invalid, so rebuild prerendered pages and purge cached HTML that contains them. Fully static sites do not need a secret.

In development, the [setup checks](/docs/nuxt-seo/getting-started/troubleshooting#setup-checks) report a missing secret as required.

### Other OG Image v7 changes

The [OG Image v7 migration guide](/docs/og-image/migration-guide/v7) covers each of these changes:

- OG Image needs Satori 0.47. Add `flex flex-col` to elements that depended on the old forced flex layout.
- OG Image needs Takumi v2 (`^2.15.0`). Replace `takumi.fetchedResources` and `takumi.persistentImages` with `takumi.images`.
- Custom fonts need `@nuxt/fonts` v1. Families used only in OG images need `global: true`.

## Step 4: Update Nuxt Content

### Use Nuxt Content v3

Nuxt SEO v6 no longer supports Nuxt Content v2. Upgrade to Nuxt Content v3. See the [Nuxt Content guide](/docs/nuxt-seo/guides/nuxt-content) for the collection setup.

### Upgrade to Zod 4

`defineRobotsSchema()`{lang="ts"}, `defineSitemapSchema()`{lang="ts"}, and `defineOgImageSchema()`{lang="ts"} require [Zod](https://zod.dev) 4. Upgrade the `zod` package in your app:

```bash
npx nypm add zod@^4
```

### Replace `asSeoCollection()`{lang="ts"}

v5 deprecated `asSeoCollection()`{lang="ts"}. v6 removes it. Compose the schema helpers in your collection schema:

```diff [content.config.ts]
import { defineCollection, defineContentConfig } from '@nuxt/content'
- import { asSeoCollection } from '@nuxtjs/seo/content'
+ import { defineOgImageSchema, defineRobotsSchema, defineSchemaOrgSchema, defineSitemapSchema } from '@nuxtjs/seo/content'
+ import { z } from 'zod'

export default defineContentConfig({
  collections: {
    content: defineCollection(
-     asSeoCollection({
-       type: 'page',
-       source: '**/*.md',
-     }),
+     {
+       type: 'page',
+       source: '**/*.md',
+       schema: z.object({
+         robots: defineRobotsSchema(),
+         sitemap: defineSitemapSchema(),
+         ogImage: defineOgImageSchema(),
+         schemaOrg: defineSchemaOrgSchema(),
+       }),
+     },
    ),
  },
})
```

## Step 5: Update Server Code

### Server hooks receive a `RequestEvent`

The `robots:*` and `sitemap:*` hook contexts now carry a `RequestEvent` from `nuxt/server`, in place of an h3 `H3Event`. Read request headers with the web API:

```diff [server/plugins/sitemap.ts]
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('sitemap:sources', (ctx) => {
-   const auth = ctx.event.node.req.headers.authorization
+   const auth = ctx.event.req.headers.get('authorization')
  })
})
```

OG Image's `getOgImageUrl()`{lang="ts"} also takes a `RequestEvent`. Import `defineEventHandler` from `nuxt/server` in handlers that call it.

### Update runtime imports

Auto imports do not change. If you import a module's runtime by hand, use the new aliases:

| Old                                                     | New                                       |
| ------------------------------------------------------- | ----------------------------------------- |
| `#internal/nuxt-robots`, `#internal/nuxt-simple-robots` | `#robots/server` or `#robots/app`         |
| `#schema-org`                                           | `#schema-org/app` or `#schema-org/server` |
| `#seo-utils`                                            | `#seo-utils/app`                          |

## Step 6: Update Site Config

Site Config v5 changes priority and values:

- `updateSiteConfig()`{lang="ts"} without `_priority` now ranks below your `site` key and `NUXT_SITE_*` variables. Pass `_priority: SiteConfigPriority.runtime` to keep the old order.
- `indexable` is now always a boolean. It defaults to `env === 'production'`.
- Module authors: Site Config v5 drops `installNuxtSiteConfig()`{lang="ts"}. Declare `nuxt-site-config` in `moduleDependencies`.

```diff [modules/site.ts]
- import { updateSiteConfig } from 'nuxt-site-config-kit'
+ import { SiteConfigPriority, updateSiteConfig } from 'nuxt-site-config-kit'

updateSiteConfig({
  url: 'https://example.com',
+ _priority: SiteConfigPriority.runtime,
})
```

## Step 7: Check Smaller Module Changes

- **Robots:** `robots.txt` files in `assets/` or `pages/` resolve from `srcDir` (`app/` in Nuxt 4). Move a root `assets/robots.txt` or `pages/robots.txt` under `app/`.
- **Robots:** `noai` and `noimageai` route rules no longer mark a route as not indexable. A `robots:config` change made during a request now affects only that request.
- **Robots:** route rules now take precedence over `definePageMeta({ robots })`{lang="ts"}. If both set a rule for a page, the route rule wins. Check pages that set both.
- **Sitemap:** Sitemap v9 drops `nuxt-simple-robots` support. Use `@nuxtjs/robots` v7.
- **Link Checker:** Reports show the real status text, and an unreachable link reports status `0`.
- **SEO Utils:** `sharp` is an optional peer dependency. Add it as a development dependency to use `nuxt-seo-utils icons`. Runtime minification no longer rewrites inline JavaScript.

## Step 8: Verify

1. Install dependencies and update the lockfile.
2. Run `npx nuxt typecheck`.
3. Run `npx nuxt build`. A module version mismatch fails here and names the module.
4. Run `npx nuxt dev`, open the home page, and read the Nuxt SEO setup checks in the terminal.
5. Check `/robots.txt`, `/sitemap.xml`, `/llms.txt`, and one OG image URL.

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
