Trailing Slashes on Netlify

EDIT April 2021: While this guide may be useful, I’ve since published more refined work pertaining specifically to Gatsby.js that you may want to read instead!

To trailing-slash, or to not trailing-slash. That is the question!!

Alright, jokes aside, this is a great question. Aside from keeping your site concise, DRY, and friendly, the trailing-slash conundrum can impact your SEO… which can impact revenues. It’s a big deal! For starters, let’s quickly clarify that and are different URLs, and if the same content is rendered at both, you’ve got duplicate content. That’s bad for SEO. Let’s fix it.

Let me start by making something clear: in the general space of SEO ratings and web principles, it doesn’t matter if we do or don’t use a trailing slash for our site. The importance lies in choosing one approach and being consistent with it across all URLs within that site. Most folks don’t care whether the site they’re viewing the content of is using a trailing slash or not; they simply care that they found the article (either from a share or Google) and that it resolves into helpful content in their browser. Trailing-slashes are for SEO and analytics.

That said, don’t fight your tooling. For reasons I’m about to dive into, Netlify is biased toward using the trailing-slash. If you’re on board with this, unifying your site to always use the trailing slash approach is absolutely feasible. If you’re on Netlify and don’t want to use the trailing-slash, you’re going to have a bad difficult time.

As both a point of impetus and reference, this article came to be while working on this site. This is a Gatsby site and I couldn’t ever quite figure out how to get the trailing / not-trailing slash working correctly, so I went down the rabbit hole. It goes deep. This article ought to provide you all you need to know for unifying your site’s trailing-slash behavior, regardless of your site being built on Gatsby, Next.js, Nuxt, Jekyll, etc.

Let’s dive in.

For starters, I want to break things down into two different perspectives. This will help clarify the mental model of what’s-going-on-where.

1) What the server responds with


2) What you see in the browser address bar

In the case of the former, 1), we need to get Netlify to only respond to the trailing-slash-variant of our content. This is especially important since web-crawlers and indexers typically don’t run our JavaScript - the static content they receive when directly calling the server is all that matters. If you have some JavaScript in place to remove the trailing slash from the browser but your server is still serving the same content on both /test and /test/, web-crawlers are going to index your content on both URLs! That’s duplication!

Luckily, Netlify offers a feature that will automatically redirect (directory) requests from /test to /test/! This is just one part of the puzzle, but we need to enable this feature so that our content will only ever be accessible on the trailing-slash variant. The feature is called “Pretty URLs” and can be found within your site’s Build & Deploy settings, under Asset Optimization. The key here is to make sure that “Disable asset optimization” is un-checked and “Pretty URLs” is checked.

Netlify's Admin interface for asset optimization
'Disabled' un-checked, 'Pretty URLs' checked!

I believe these settings are cemented on the next deploy, so if you feel the need to verify, go ahead and deploy a new version of your app then pull out your favorite command-line HTTP request tool (mine’s httpie, but feel free to use cURL or anything else). Since we’re specifically checking the server response behavior, there’s no need to load anything in the browser yet. Here’s what it looks for this site now that I’ve made that change.

When I hit the un-slashed URL, I get redirected to the slashed URL:

A command line request to this website's blog page without a trailing slash, getting redirected
Forced to the slash version!

And subsequently when I request the slashed version from the get-go:

A command line request to this website's blog page with a trailing slash, not getting redirected
Nice! Content only available on one URL

Great. So now Netlify’s servers are forcing content to a single URL. That’s all we need to do on the Netlify side! Let’s dig into part 2).

2) What you see in the browser address bar

Or perhaps ‘the address bar can be deceiving’. See, once a webpage is loaded from the initial server request described above, it’s the content of that page that shapes the user’s next steps. If that page has an anchor <a> to /blog (on a pure static site), clicking that link will force the user to hit the /blog ➡ /blog/ redirection behavior we just setup above. That’s an extra round-trip to the server for any link the user wants to click on. No good!

Unfortunately, this does mean that we (as the site developers) need to go through and update all of the <a> anchors across the site and ensure that they’re targeting a trailing-slash target… at least for links on our own site. This also includes any special internal-links that PWA-based SSGs use for versatile client-side routing: <Link to="/test/" (Gatsby), <Link href="/test/" (Next.js), and <NuxtLink to="/test/" (Nuxt), for examples. This also includes any site-workflow buttons or links - things like pagination buttons and/or tags buttons etc. Don’t forget any Markdown content you might have as well: [my blog](/blog/) and/or any external sources (WordPress, GraphQL, Contentful, etc.) that may have rich-text / HTML content you serve. All of these anchors need to be updated to the trailing-slash target. Even if you’re okay with your users needlessly hitting the server for an additional roundtrip just to be told to add a slash, these updated anchor target values get embedded into the json page-data that the PWA relies on once it’s been hydrated client-side, and that data not containing the trailing-slash variant of your anchor targets can cause problems.

Finally, we need to make sure that our SSG’s generated pages are also converted to ‘trailing-slash format’. In this case, that means that our SSG is generating index.html pages within a named folder rather than simply a named html page. This calls back to historical web parlance, where the path in a URL implied a directory if it ended with a slash and implied a document if it did not.

The idea is this: if you have a document, /blog/yay-its-summer.html (remember that summer.html is just a document like any other), Netlify’s Pretty URLs feature that we enabled in part 1) will prettify that path to /blog/yay-its-summer… note the distinct lack of trailing slash there. That’s because the path specifies a document, not a directory. While a great feature, Pretty URLs respects web parlance 😅. The fix is to convert the document to a directory. In this case, changing /blog/yay-its-summer.html to /blog/yay-its-summer/index.html. Now Pretty URLs will prettify that path to /blog/yay-its-summer/! That’s what we want! Pretty URLs still adheres to document vs. directory parlance, so we need to adjust our SSGs to generate content according to that parlance as well.

There isn’t exactly cut-and-dry advise on how to get your SSG running in this way, though. For instance, Gatsby generates pages automatically for any file under ./src/pages/, but Gatsby is also widely built around the createPages() API - meaning that pages can be built both from Gatsby’s automatic page-detection and your theme’s implementation of createPages(). The open-source plugin gatsby-plugin-force-trailing-slashes may provide the functionality for nesting all resulting documents inside of directories (make sure you put it last in your plugins array), but you’ll want to take special care to ensure that any pages created by the createPages() API are converted to index.htmls nested within directories. Remember that you can always run gatsby build locally and inspect the resulting ./public/ directory.

On the other hand, Next.js and Nuxt both have config values that can enable and force trailing slashes on all routes. Neat! I’ll bet money it’s doing the same directory-nesting I just described… Nuxt even warns that all internal links need to be updated to the trailing-slash target, so thank goodness we already did that 😉

For the non-PWA SSG users out there, Jekyll’s routing configuration can help you out, but simply reconfiguring your site’s file structure can often make the difference.

Once you’ve figured out how to correctly nest your SSG’s resulting documents within directories

That’s it!

With your documents correctly converted to directories, all of your anchor targets updated, and Netlify’s Pretty URLs feature activated for your site, you can now confirm that all of your content is uniquely accessible from one (non-duplicated) trailing-slash URL, and that all of your content adheres to the trailing-slash system! Do some checks (both on command line and the browser) to make sure all of your links look like they’re going to the right places, double check that you didn’t miss any fancy PWA internal-navigation-Links, and rejoice! You’ve now conquered the trailing-slash-conundrum. Huzzah.


P.S. Did I miss something? Still having trouble? Comment below and we can work it out!

Header Photo / Banner by: @nh7_

Join the Conversation

Latest Blog Posts