DevelopmentJuly 17, 2026

Gatsby: to slash or not to slash?

Like most junior web developers, I had little to no idea what a trailing slash actually was. Of course I used it in projects, but more out of common sense (or even intuition) than because I truly understood the concept.

The trailing slash is like the Oxford comma: unless you've deliberately looked into the topic, you'll most likely scatter it around going purely on intuition.

The trailing slash is the forward-slash character placed at the end of a URL. The general convention (backed by web standards) says that if a URL ends in a slash, it points to a directory; if not, it points to a file.

File nameResulting fileRendered URL
blog.htmlblog.htmlmysite.io/blog
blog.jsblog/index.htmlmysite.io/blog/
index.js/index.htmlmysite.io/

Be consistent with how you use the trailing slash across your projects and with your team, because most of the time it's up to you to decide when and how to use it.

Do I really need to worry about this?

Yes! It can cause SEO and performance optimization problems.

First, you don't want a user browsing /thisUrl to be redirected to /thisUrl/ with no explanation whatsoever.

Second, if the same site has two URLs coexisting, one with a trailing slash and one without, it's hard to link them correctly, and on top of that it dilutes the link authority Google assigns to your site.

Here's an example of trailing slash misuse: two pages, one with a slash and one without, coexisting with no redirect between them and no explanation at all:

Another example: the video below shows a poor user experience, where the host adds the trailing slash and then the front-end router of an SPA removes it, so the user sees the URL change and ends up fairly confused.

So, for a web crawler to understand you correctly, you need to make your intentions clear with canonical URLs or redirects. From there, you can have as many identical pages as you want.

How do trailing slashes work when Gatsby builds the site? What happens inside the createPage() function?

This is a typical example of the file tree of a Gatsby project with the templates pages folder:


├── src
│ ├── templates
│ │ ├── 404.jsx
│ │ ├── blog.jsx
│ │ ├── demo.jsx
│ │ ├── index.jsx
│ │ └── product.jsx
│ └── html.jsx
├── gatsby-browser.js
├── gatsby-config.js
└── gatsby-node.js

And this is the corresponding public folder, with all the pages built:


├── public
│ ├── blog
│ │ ├── index.html
│ ├── demo
│ │ ├── index.html
│ ├── product
│ │ ├── index.html
├── 404.html
└── index.html

Let's look at how this page-building process works and what you need to know about trailing slashes to fully understand the mechanics.

The createPage() function receives and processes the path argument. This can happen "automatically," when the page is generated from a .js file in the src/pages/* folder, or programmatically, when the desired route is explicitly passed in the call to createPage() inside gatsby-node.js. Either way, the path string in createPage() tells Gatsby exactly where to build the HTML.


createPage({
path: `/my-path/`,
component: path.resolve(`./src/templates/my-path.js`),
});

For example, if your src/pages/* folder contains a file called product.js, /product is the route where the HTML will be built. And here's the reason you should use trailing slashes in Gatsby: the path string passed to createPage() becomes a directory, and the resulting HTML ends up at /product/index.html. The same happens with any path you pass, whether or not it ends in a slash.

You could say Gatsby generates folders, not files, with just three exceptions: routes explicitly ending in .html passed to createPage() in gatsby-node.js, the 404 page, which is generated at public/404.html, and the site index.


createPage({
path: `/404/`,
component: path.resolve(`./src/templates/404.js`),
}); // the 404 page is going to be created at public/404.html

What's the difference between blog.html and blog/index.html on the client side?

This isn't a very common case, because in real projects almost nobody uses routes like blog.html, but I think it's essential to have a full picture of all the possible options.

As we've seen, page generation produces different results depending on whether the route passed to createPage() ends in .html or not. And that also causes differences on the client side. path='/blog.html' generates blog.html, and that page is available at mysite.io/blog.

By contrast, path='/blog' or path='/blog/' generate the HTML at /blog/index.html, and that page should be available at mysite.io/blog/. If this isn't done properly, it can confuse the user and cause SEO problems from duplicate content, so, once again, the best approach is to be consistent when generating pages.

Don't forget that, since /blog and /blog/ generate the same /blog/index.html, from a web-standards point of view you should always use the trailing slash (i.e., '/blog/') when creating pages programmatically or adding permalinks on the page.


createPage({
path: `/blog`,
component: path.resolve(`./src/templates/blog.js`),
});


createPage({
path: `/blog.html`,
component: path.resolve(`./src/templates/blog.js`),
});

Trailing slash configuration in Gatsby: where things stand now and what the future holds

For now there's no explicit standardization, and you'll notice that Gatsby doesn't enforce trailing slash usage in your projects, either in v3 or v4. In versions before 4.7, the solution was to use plugins (gatsby-plugin-force-trailing-slashes or gatsby-plugin-remove-trailing-slashes).

However, since Gatsby 4.7 there's a trailingSlash option, which you can set in your project's gatsby-config.js instead of using an extra plugin. It has three values:

  • always: always adds the trailing slash to every URL, for example, from /x to /x/.
  • never: removes all trailing slashes from every URL, for example, from /x/ to /x.
  • ignore: doesn't modify the URL automatically.

module.exports = {
trailingSlash: `always`,
};

In Gatsby v3/v4, the default value is legacy, so as not to break anything in existing projects.

In Gatsby v5, the default value for this option will be always, meaning Gatsby will commit to explicit trailing slash usage.

The table below shows how Gatsby Cloud (and Gatsby itself with gatsby develop and gatsby serve) handles redirects depending on the trailingSlash value:

The example page is at https://example.io/blog/index.html.

trailingSlash valueRequested URLRedirect codeRendered URL
ignore/blognone/blog
ignore/blog/none/blog/
ignore/blog/index.htmlnoneblog/
always/blog301/blog/
always/blog/none/blog/
always/blog/index.htmlnone/blog/
never/blognone/blog
never/blog/301/blog
never/blog/index.htmlnone/blog

Keep in mind that this configuration option only affects client-side behavior; actual server-side behavior still depends on your hosting provider or your web server's configuration.

Bonus: how do different platforms handle redirects with and without a trailing slash?

There's a great page by @slorber that summarizes deploying a basic static site across different hosting platforms.

Their thorough table shows how different platforms (Vercel, Cloudflare Pages, GitHub Pages, Netlify) handle redirects for this simple static site:


static
│
├── file.html
│
├── folder
│ └── index.html
│
├── both.html
└── both
└── index.html

The main takeaway is that the confusion and unpredictability come from the fact that there's no common convention among hosting providers and static site generators for handling trailing slashes in a standardized way. So the solution to all these potential problems is to be consistent with trailing slashes, for example, by using Gatsby 4.7 or later with the trailingSlash: 'always' setting.

Summary

Understanding the trailing slash is essential for any web developer, but we usually only pay attention to it when something breaks in SEO or in the site's redirects. I hope this article has cleared up the basics of working with trailing slashes and URLs, and that it helps you make better decisions in your day-to-day as a developer.

Useful links

Trailing slashes on Netlify

Trailing slash guide: hosting providers

Trailing slashes on URLs: controversial or settled?

RFC on trailingSlash in Gatsby

Tell us about your case

Get in touch