---
title: "Trailing Slashes in Nuxt"
description: "Choose a trailing slash convention, configure NuxtLink and canonical URLs, and test server redirects."
canonical_url: "https://nuxtseo.com/learn-seo/nuxt/routes-and-rendering/trailing-slashes"
last_updated: "2026-10-05"
---

::key-takeaways
- Use trailing slashes for static file servers, legacy URL migrations, or CMS conventions
- Configure NuxtLink globally with `experimental.defaults.nuxtLink.trailingSlash`
- Set `site.trailingSlash` for SEO modules, then verify links and redirects separately
::

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 // URL preference for modules that use Site Config
  }
})
```

## Why Enable Trailing Slashes

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

- Static file servers such as Apache, Nginx, and S3 treat trailing slashes as directories, serving `/about/` as `/about/index.html`. Skip the slash and some of them return 404s or need extra config
- If you're migrating from WordPress, Rails, or another legacy CMS that used trailing slashes, matching the existing format avoids mass redirects
- If your team uses a trailing-slash convention in CMS path data, apply that convention consistently in the app
- Team convention: some developers use the trailing slash to visually mark a URL as a section

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">`{lang="html"} 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

If `/about` and `/about/` serve equivalent content, they create duplicate URLs. Google can consolidate them automatically. Consistent redirects and canonical signals express your preferred URL.

Configure three parts:

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'
      }
    }
  },
})
```

This canonical example covers parameter-free pages. If a query parameter changes content, preserve it according to your URL policy, including pagination:

```vue
<script setup lang="ts">
const route = useRoute()

useHead(() => ({
  link: [{ rel: 'canonical', href: `https://example.com${route.path}${route.path.endsWith('/') ? '' : '/'}` }]
}))
</script>
```

This works but doesn't scale.

### Nuxt SEO Setup

[Nuxt SEO](/docs/nuxt-seo/getting-started/introduction) shares the URL preference with its SEO modules:

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

Verify the configured modules' output:

- Appends trailing slashes to all [sitemap](/docs/sitemap/getting-started/introduction) URLs
- Sets [canonical URLs](/docs/seo-utils/guides/canonical-url) with trailing slashes
- Check generated [OG image](/docs/og-image/getting-started/introduction) URLs with your configured renderer

::module-card{slug="nuxt-seo" .w-1/2}
::

## 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

Do not assume an exact `/about` rule excludes `/about/`. Nitro can match both and redirect the destination to itself. Use pathname-aware middleware below, or your host's tested URL normalization.

### Using Server Middleware

This example normalizes non-file page paths. Confirm these exclusions match your application. It preserves the query string and leaves the slash destination unchanged:

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

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

## Static Hosting and Prerendering

When prerendering, Nuxt generates `/about` as `/about/index.html` by default. Your static host determines whether `/about` redirects to `/about/`. Check the deployed response.

If you want trailing slashes, this is correct behavior.

Set `autoSubfolderIndex: false` to generate `/about.html` instead. This changes output layout; your host still controls URL redirects:

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

## Checklist

::checklist{#nuxt-trailing-slashes}
- Decide on trailing slashes or no trailing slashes for the whole site
- Set `site.trailingSlash` for SEO modules; configure and verify NuxtLink and redirects
- Verify wrong-format URLs redirect once, preserve queries, and terminate at a successful page
- Confirm canonical URLs match your chosen format
- Check prerendered output matches your trailing slash setting (`autoSubfolderIndex`)
::

## Sitemap

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