trailingSlash
Redirects URLs to add or remove a trailing slash, and changes exported filenames.
trailingSlash?: boolean- Added in Next.js v9.5.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$trailingSlashoptional | boolean= false | false redirects /about/ to /about. true reverses it, redirecting /about to /about/. Static file URLs with an extension and anything under .well-known/ are exempt from the trailing slash in either direction. |
Changelog
- PHP v13.1.0skipTrailingSlashRedirect added, allowing the redirect to be disabled and handled in proxy (then middleware) instead.
- PHP v9.5.0trailingSlash added.
Next.js picks one canonical URL shape and redirects the other to it. By default that is the slashless form: /about/ becomes /about. Setting trailingSlash: true flips the rule so /about becomes /about/.
When to reach for it
- Matching an existing site's URLs during a migration so links and analytics keep lining up.
- Producing directory-style
index.htmlfiles for a static host. - Fixing duplicate-content warnings from two spellings of one page.
What trips people up
The redirect is real traffic. Every link written in the non-canonical form costs an extra round trip, and pointing a CDN or load balancer at the wrong form on top of this setting produces a redirect loop. External rewrites need the slash written into source too, or they stop matching. With output: 'export' the setting changes the emitted filenames — /about/index.html instead of /about.html — which is what most static hosts expect. Two path shapes are always exempt: URLs ending in a file extension, and anything under .well-known/. For per-path control during a partial migration, set skipTrailingSlashRedirect: true and do the redirect yourself in proxy.ts.
Example
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
trailingSlash: true,
}
export default nextConfigWhen to use it
- Match a legacy site's slash convention so inbound links do not bounce
- Emit /about/index.html for a static host that serves directories
- Keep only one canonical URL per page for search engines
- Migrate a subset of paths by handling slashes in proxy.ts
Frequently asked questions
Does trailingSlash change my routes or just the URLs?
Just the URLs. Route files are unchanged; Next.js issues a redirect to whichever spelling you declared canonical. With output: 'export' it also changes the emitted filenames to /about/index.html instead of /about.html.
Which URLs are exempt from the trailing slash?
Static file URLs with an extension and anything under .well-known/. So /file.txt, images/photos/picture.png and .well-known/subfolder/config.json keep their shape with trailingSlash: true.
How do I keep trailing slashes on only some paths?
Set skipTrailingSlashRedirect: true, which disables the built-in redirect, then implement the rule you want in proxy.ts. This is the documented pattern for incremental migrations where legacy prefixes must keep their old URL shape.
More examples
Trailing slashes with an external rewrite
With trailingSlash: true the request reaching the rewrite layer already ends in a slash, so a source of '/blog' would never match. The destination carries a slash as well because the upstream server expects one; drop it there and the proxied origin may issue its own redirect, which the browser sees as a second hop.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
trailingSlash: true,
async rewrites() {
return [
{
source: '/blog/',
destination: 'https://example.com/blog/',
},
{
source: '/blog/:path*/',
destination: 'https://example.com/blog/:path*/',
},
]
},
}
export default nextConfigPer-path control via skipTrailingSlashRedirect
skipTrailingSlashRedirect turns off the built-in redirect entirely, leaving the decision to proxy.ts. Here /docs and /blog keep whatever shape the legacy system used while everything else is normalised to a trailing slash. The regex reproduces the built-in exemptions for file extensions and .well-known paths, which you must handle yourself once the automatic behaviour is off.
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
skipTrailingSlashRedirect: true,
}
export default nextConfig
// proxy.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const legacyPrefixes = ['/docs', '/blog']
export default async function proxy(req: NextRequest) {
const { pathname } = req.nextUrl
if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {
return NextResponse.next()
}
if (
!pathname.endsWith('/') &&
!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
) {
return NextResponse.redirect(new URL(`${pathname}/`, req.nextUrl))
}
}
Discussion