Trailing Slashes in Nuxt
- Use trailing slashes for static file servers, legacy URL migrations, or CMS conventions
- Configure NuxtLink globally with
experimental.defaults.nuxtLink.trailingSlash - Set
site.trailingSlashfor 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.
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. Set it globally or per-link.
Global Configuration
Apply trailing slashes to all internal links:
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:
<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:
- Consistent internal links
- Correct canonical URLs
- Redirects for the wrong format
Manual Setup
Without Nuxt SEO, configure each piece separately:
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:
<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 shares the URL preference with its SEO modules:
export default defineNuxtConfig({
modules: ['@nuxtjs/seo'],
site: {
url: 'https://example.com',
trailingSlash: true
}
})Verify the configured modules' output:
- Appends trailing slashes to all sitemap URLs
- Sets canonical URLs with trailing slashes
- Check generated OG image URLs with your configured renderer
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:
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:
export default defineNuxtConfig({
nitro: {
prerender: {
autoSubfolderIndex: false
}
}
})Checklist
- Decide on trailing slashes or no trailing slashes for the whole site
- Set
site.trailingSlashfor 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)
Pre-launch setup, post-launch verification, and ongoing monitoring. Interactive checklist with links to every guide.