trailingSlash

Redirects URLs to add or remove a trailing slash, and changes exported filenames.

Signature
trailingSlash?: boolean
  • Added in Next.js v9.5.0

Parameters

ParameterTypeDescription
$trailingSlashoptionalboolean= falsefalse 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.html files 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

Example · typescript
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  trailingSlash: true,
}

export default nextConfig

When 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.

Example · typescript
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 nextConfig

Per-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.

Example · typescript
// 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

  • Be the first to comment on this lesson.