Zach’s ugly mug (his face) Zach Leat­herman

Trailing Slashes on URLs: Contentious or Settled?

#1222k 09 January 24, 2022

After some discussion with IndieWeb Avatar for https://whitep4nth3r.com/Salma last week, I decided it was worthwhile to do a deep dive on Trailing Slashes in URLs. More specifically, which of these should I be using?

  • http://zachleat.com/resource
  • http://zachleat.com/resource/

I did what any curious but self-doubting person might do in this situation. I posted a Twitter poll. The results surprised me!

But before we go much further, let’s go over the problems we’re trying to solve:

  1. Performance: when you leave off a trailing slash and the platform expects one (or vice versa), you get a redirect which is a performance no-no.
  2. SEO: if your content exists at two (or more!) distinct URL endpoints, it is a SEO no-no. SE-no-no. SEO-apolo-graphql-anton-ohno (I apologize for nothing). *Ahem*. You need redirects.
  3. Asset References: if your markup uses relative paths to reference assets (e.g. <img src="image.avif">), these URLs may break if your host isn’t aggressive enough with redirects to a canonical home base.
  4. Cool URIs Don’t Change: we want to avoid including any file extension in our URLs.

At the end, the most important piece to remember here is that consistency is king. No matter which approach you use for a specific resource (trailing slash or sans the slash), it should be the canonical version and it should be used everywhere (even when third parties link to your site). Any other non-canonical version of the URL should (ideally) redirect to the canonical version.

Interestingly, some of my surprise at current sentiment was that developers sometimes use different strategies for different types of content within the same project! That was something I did not expect and am curious how well that is supported by tooling.

Perspectives

I think the leaky part of the poll in question is that there are a bunch of different perspectives to this problem:

  1. Developers, wanting to implement a personal or team preference.
  2. App/site/framework tooling (e.g. say, uh, you’re the maintainer of Eleventy)
  3. Platform (e.g. Netlify—casting a wide net and thinking what works best across as many tools and frameworks as possible)

Disclosure: I am both an employee of Netlify and the creator/maintainer of Eleventy.

IndieWeb Avatar for https://sebastienlorber.com/Sebastien Lorber has put together an incredible repository of research results showing how this works on a variety of popular hosts and static site generators. I’ll reference this data throughout this post. Sebastien also included results for a variety of different configuration options on those different platforms. I simplified to platform-default behavior for this post.

Writing resource/index.html

Gatsby, Docusaurus, NuxtJS, and Eleventy all use folder generated resource/index.html files to offer an easy and portable way to use trailing slashes by default.

The default filename index.html is a convention that’s pretty safely cemented in web history at this point. It represents the file shown when a file name is not specified in the URL. Citations from Apache, NGINX, LiteSpeed, Microsoft IIS.

Here’s what happens when a web browser makes a request to a URL representing this content:

  • /resource
    • ✅ GitHub Pages, Netlify, and Cloudflare Pages redirect to the trailing slash /resource/ as expected.
    • 🟡 Warning: Vercel, Render, and Azure Static Web Apps: slashless /resource returns content but without redirects, resulting in multiple endpoints for the same content.
  • /resource/
    • ✅ All hosts agree that /resource/ should return content from resource/index.html
  • 💔 Warning: If you’re using relative resource URLs, the assets may be missing on Vercel, Render, and Azure Static Web Apps (depending on which duplicated endpoint you’ve visited).
    • <img src="image.avif"> on /resource/ resolves to /resource/image.avif
    • <img src="image.avif"> on /resource resolves to /image.avif

Writing resource.html

Both Jekyll and Next.js take a different approach. They output resource.html instead of index.html files.

Here’s what happens when a web browser makes a request to a URL representing this content:

  • /resource
    • ✅ Almost everyone agrees that /resource should return content from resource.html
    • 🆘 Warning: Confusingly Vercel is the only host tested that returns a HTTP 404 error for /resource.
  • /resource/
    • ✅ Netlify and Cloudflare Pages redirect to the slashless /resource.
    • 🆘 Warning: GitHub Pages, Vercel, and Azure Static Web Apps all return a HTTP 404 error. I’ll admit this one is a little more contentious. I won’t take a hardline here—I can see the reasoning behind it. But I do consider it better to redirect than 404.

⚠️ Writing Both resource.html and resource/index.html

There exists an even edgier edge case here. What happens when resource.html and resource/index.html both exist in a project?

  • /resource
    • ✅ Everyone agrees that /resource should return content from resource.html
  • /resource/
    • ✅ Almost everyone agrees that /resource/ should return content from resource/index.html
    • 🆘 Warning: Netlify redirects to /resource instead.

More seriously, I think this case actually represents a larger URL usability problem for the content. In this case, though pedantically and technically correct, /resource and /resource/ confusingly resolve to different pieces of content. I think this should be avoided if at all possible and a tooling error is warranted. It could be argued that Netlify takes an opinionated stance here to attempt to resolve the ambiguity at a platform level.

IndieWeb Avatar for https://www.11ty.dev/Eleventy Specific Note

Eleventy users can rest easy: because input files resource.html and resource/index.html both write to the output directory at _site/resource/index.html by default, we throw a DuplicatePermalinkOutputError error to mitigate this for you. (You can force the issue using permalink if you really want)

Results Table

Here’s a summary table of the above findings, leaving off the (in my opinion) flawed Writing Both case above.

Legend:

  • 🆘 HTTP 404 Error
  • 💔 Potentially Broken Assets (e.g. <img src="image.avif">)
  • 🟡 SEO Warning: Multiple endpoints for the same content
  • ✅ Correct, canonical or redirects to canonical
resource.html resource/index.html
Host /resource /resource/ /resource /resource/
GitHub Pages 🆘 404 ✅➡️ /resource/
Netlify ✅➡️ /resource ✅➡️ /resource/
Vercel 🆘 404 🆘 404 🟡💔
Cloudflare Pages ✅➡️ /resource ✅➡️ /resource/
Render 🟡💔 🟡💔
Azure Static Web Apps 🆘 404 🟡💔

So, what?

Ideally, (speaking as the maintainer of Eleventy) folks working on developer tooling should craft tools to create output that uses existing conventions and can be portable to as many hosts in as many different hosting environments as possible.

That being said, given the above information it seems clear to me that resource/index.html is marginally safer than resource.html for tooling (on the premise that resolved but duplicated content with potentially missing assets is better than a 404 error 😅).

What’s more, I think it is the unique job of our development tools to help diagnose and mitigate future production problems. My (very biased) opinion is that more frameworks and tools should take a harder line in preventing confusingly similar but distinct URLs in a project. It is a usability error to have resource.html (output to /resource) and resource/index.html (output to /resource/) fighting over the same URL in the same project, and we should treat it as such.


< Older
CSS-only External Link Favicons
Newer >
Back to the Facepile, Featherweight Edition

Zach Leatherman IndieWeb Avatar for https://zachleat.com/is a builder for the web at Font Awesome and the creator of Build Awesome (née IndieWeb Avatar for https://www.11ty.devEleventy/11ty), an award-winning open source website generator. He measures website performance with speedlify and at one point became too fixated on web fonts. He has given 89 talks in nine different countries at events like Beyond Tellerrand, Smashing Conference, Jamstack Conf, CSSConf, and The White House. Formerly part of CloudCannon, Netlify, Filament Group, NEJS CONF, and NebraskaJS. Learn more about Zach »

9 Likes
mzunguGermanySergioAgentMeterPDFopsCharity GambleKhalid ⚡LorenAmelangAlesandro Ortiz ????????????️‍????
9 Comments
  1. Alesandro Ortiz ????????????️‍????
    Alesandro Ortiz ????????????️‍????

    @zachleat I haven't noticed any changes recently. Quick testing shows non-trailing-slash paths redirect to trailing-slash path on a couple of my Netlify sites. Haven't deployed in a few days, but not sure if that would change behavior.

  2. Dean Leigh
    Dean Leigh

    Hi @zachleat.com - I noticed these word breaks and wondered if hyphens: auto; reduce readability in certain contexts? No criticism, just curios if it's accessibly has been documented for it's use in paragraph text.

  3. Zach Leatherman
    Zach Leatherman

    Oh, huh, after git blaming that it’s about 9 years old (we may never know what user was responsible for it though *ahem*) Agree though that it feels aggressive! Gonna revert it

  4. Dean Leigh
    Dean Leigh

    Hehe I have thought about it for headings in the past but modern CSS and fluid typography seems to address most cases now.

  5. Khalid ⚡
    Khalid ⚡

    @zachleat @AlesandroOrtiz It's a battle I wage in my mind all the time. It's not settled. Good slashes died out there maaaaaan!

  6. Alesandro Ortiz ????????????️‍????
    Alesandro Ortiz ????????????️‍????

    @zachleat Hm, I am seeing the issue on my personal site: https://alesandroortiz.com/articles should redirect to /articles/ but doesn't. I'm pretty sure it did before. But on a couple other sites that I tried earlier, it does redirect with a 301. Will check what might be … Truncated

  7. Alesandro Ortiz 🇵🇷🏳️‍🌈
    Alesandro Ortiz 🇵🇷🏳️‍🌈

    @zachleat Ok, the difference is pretty URLs is enabled on the ones that _do_ redirect, and disabled on the ones that _don't_ redirect. Seems like the same you are seeing. They all use 11ty with `/dir/index.html` output (so /articles/ on my personal site is /articles/index.h… Truncated

  8. Zach Leatherman
    Zach Leatherman

    @AlesandroOrtiz might need to cheekily ping the friendly @eduardoboucas on this one?

  9. Alesandro Ortiz ????????????️‍????
    Alesandro Ortiz ????????????️‍????

    @zachleat I'll have you reach out to Netlify folks since it's not broken on my prod sites where it would be a breaking change. And I'm on free plan so I can't ping support directly either. Would be great to have it fixed on my personal site, but not critical ther… Truncated

Shamelessly plug your related post

These are webmentions via the IndieWeb and webmention.io.

Sharing on social media?

This is what will show up when you share this post on Social Media:

How did you do this? I automated my Open Graph images. (Peer behind the curtain at the test page)