Trailing Slashes in Nuxt

Learn when to enable trailing slashes, how to configure NuxtLink, and the one-line Nuxt SEO setup.
Harlan WiltonHarlan Wilton5 mins read Published Updated
What you'll learn
  • 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

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.

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.

NuxtLink has built-in trailing slash support. Set it globally or per-link.

Global Configuration

Apply trailing slashes to all internal links:

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

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

Override the global setting on specific links:

<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
  3. Redirects for the wrong format

Manual Setup

Without Nuxt SEO, configure each piece separately:

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:

<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 handles all of it with one config:

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

This single option:

Nuxt SEO v3.3.0
2.2M
1.3K
The all-in-one module that brings it all together.

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

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:

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:

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