---
title: "Trailing Slashes in Nuxt"
description: "Learn when to enable trailing slashes, how to configure NuxtLink, and the one-line Nuxt SEO setup."
canonical_url: "https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/trailing-slashes"
last_updated: "2026-01-07"
---

<key-takeaways>

- Use trailing slashes for static file servers, legacy URL migrations, or CMS conventions
- Configure NuxtLink globally with `experimental.defaults.nuxtLink.trailingSlash`
- Nuxt SEO handles sitemaps, canonicals, and OG URLs with one config: `site.trailingSlash: true`

</key-takeaways>

A trailing slash is the `/` at the end of a URL. `/about/` has one, `/about` doesn't. Both can serve identical content, and that's the problem.

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  site: {
    trailingSlash: true // handles everything if using Nuxt SEO
  }
})
```

## Why Enable Trailing Slashes

Most Nuxt sites don't need trailing slashes. But there are cases where you'll want them:

**Static file servers** treat trailing slashes as directories. Apache, Nginx, and S3 serve `/about/` as `/about/index.html`. Without the trailing slash, some servers return 404s or require extra config.

**Legacy URL structures** from WordPress, Rails, or older CMSs often used trailing slashes. If you're migrating, matching the existing format avoids mass redirects.

**CMS conventions** in tools like Contentful, Sanity, or Storyblok may generate paths with trailing slashes. Matching their format keeps URLs predictable.

**Team preference** sometimes dictates format. Trailing slashes visually indicate "this is a section" to some developers.

If none of these apply, stick with Nuxt's default: no trailing slashes.

## Configuring NuxtLink

NuxtLink has [built-in trailing slash support](https://nuxt.com/docs/api/components/nuxt-link#overwriting-defaults). Set it globally or per-link.

### Global Configuration

Apply trailing slashes to all internal links:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  experimental: {
    defaults: {
      nuxtLink: {
        trailingSlash: 'append' // or 'remove'
      }
    }
  }
})
```

Every `<NuxtLink to="/about">` now renders as `/about/`.

### Per-Link Override

Override the global setting on specific links:

```vue
<template>
  <!-- Forces trailing slash regardless of global config -->
  <NuxtLink to="/api/docs" trailing-slash="append">
    API Docs
  </NuxtLink>

  <!-- Removes trailing slash regardless of global config -->
  <NuxtLink to="/blog" trailing-slash="remove">
    Blog
  </NuxtLink>
</template>
```

Use this for external integrations or API routes that require a specific format.

## SEO Configuration

Trailing slashes become an SEO problem when both `/about` and `/about/` exist. Search engines see two pages with identical content, splitting your ranking signals.

You need three things:

1. Consistent internal links
2. Correct [canonical URLs](/learn-seo/nuxt/controlling-crawlers/canonical-urls)
3. [Redirects](/learn-seo/nuxt/controlling-crawlers/redirects) for the wrong format

### Manual Setup

Without Nuxt SEO, configure each piece separately:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  // 1. NuxtLink trailing slashes
  experimental: {
    defaults: {
      nuxtLink: {
        trailingSlash: 'append'
      }
    }
  },
  // 2. Redirects for wrong format
  routeRules: {
    // Redirect /about to /about/
    '/about': { redirect: '/about/' },
    '/blog': { redirect: '/blog/' }
    // ... every route
  }
})
```

Then set canonicals manually on each page:

```vue
<script setup lang="ts">
const route = useRoute()
const canonicalUrl = `https://example.com${route.path}${route.path.endsWith('/') ? '' : '/'}`

useHead({
  link: [{ rel: 'canonical', href: canonicalUrl }]
})
</script>
```

This works but doesn't scale.

### Nuxt SEO Setup

[Nuxt SEO](/docs/nuxt-seo/getting-started/introduction) handles all of it with one config:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  modules: ['@nuxtjs/seo'],
  site: {
    url: 'https://example.com',
    trailingSlash: true
  }
})
```

This single option:

- Appends trailing slashes to all [sitemap](/docs/sitemap/getting-started/introduction) URLs
- Sets [canonical URLs](/docs/seo-utils/guides/canonical-url) with trailing slashes
- Formats [OG image](/docs/og-image/getting-started/introduction) URLs correctly

<module-card className="w-1/2" slug="nuxt-seo">



</module-card>

## Redirects for Wrong Format

Even with correct internal links, external sites and old bookmarks may use the wrong format. Set up server-side redirects.

### Using Route Rules

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  routeRules: {
    // If using trailing slashes, redirect non-trailing to trailing
    '/about': { redirect: { to: '/about/', statusCode: 301 } },
    '/blog': { redirect: { to: '/blog/', statusCode: 301 } }
  }
})
```

### Using Server Middleware

For dynamic redirects across all routes:

```ts [server/middleware/trailing-slash.ts]
export default defineEventHandler((event) => {
  const path = event.path
  // Skip API routes and files with extensions
  if (path.startsWith('/api') || path.includes('.'))
    return

  // Redirect non-trailing to trailing
  if (!path.endsWith('/')) {
    return sendRedirect(event, `${path}/`, 301)
  }
})
```

## Static Hosting and Prerendering

When prerendering, Nuxt generates `/about` as `/about/index.html` by default. Static hosts like Cloudflare Pages then redirect `/about` → `/about/` with a 308.

If you want trailing slashes, this is correct behavior.

If you don't want trailing slashes and are seeing unwanted 308 redirects, set `autoSubfolderIndex: false` to generate `/about.html` instead:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  nitro: {
    prerender: {
      autoSubfolderIndex: false
    }
  }
})
```
