Right, let’s talk URLs. This is where your content meets the world, and frankly, it’s where a lot of otherwise brilliant sites face-plant. A clean, predictable URL structure isn’t just SEO catnip; it’s a promise to your users. It says, “I know where you are, I know how you got here, and I know how to get you back.” Let’s make sure you can keep that promise.

The Humble slug Field

Think of the slug as the final, URL-safe version of your content’s title. You give it My Epic Blog Post! and it gives you back my-epic-blog-post. It’s the content’s fingerprint for the web. The beauty of letting Eleventy generate this for you is consistency. You define the rules once, and every piece of content follows them.

By default, Eleventy derives the slug from your filename. my-post.md becomes /my-post/. But you’ll often want to override this, and that’s where the slug front matter comes in. It’s your escape hatch for those times when the filename is 2023-01-01-project-update.md but you want the URL to be /blog/project-launch.

---
title: "2023 Q1: Why We Pivoted to Selling Artisanal Toast"
slug: "blog/artisanal-toast-pivot"
---
<!-- This file might be at posts/2023-01-01-toast.md -->
<!-- But the URL will be /blog/artisanal-toast-pivot/ -->

Pitfall #1: Don’t try to be clever and include the full path here. The slug is just the last part. We’ll handle the full path next. Also, avoid slashes at the beginning or end—Eleventy will add them for you and you’ll just end up with double-slashes, which look sloppy.

Building the Full URL with url

If the slug is the name of your house, the url is its full address, including the street and city. This is where you define the entire output path for this piece of content. The magic here is that Eleventy is smart enough to combine a directory-based data file (like a tags.json in your posts/ folder) with the individual file’s slug.

Let’s say you have a blog. You want all your posts to live under /blog/. The simplest way is to use a directory data file.

  1. Create a posts directory.
  2. Inside it, create a posts.json file.
  3. In that JSON file, set a default url for everything in the folder.
// posts/posts.json
{
  "tags": "post",
  "url": "/blog/{{ page.fileSlug }}/"
}

Now, every Markdown file in /posts will have its URL prefixed with /blog/. The page.fileSlug is Eleventy’s built-in variable for the slug derived from the filename (without the extension).

But wait, what if you overrode it with a front matter slug? Eleventy’s got you covered. It’s hierarchy-aware. The front matter url wins, then the computed one from data, then the one from the slug. It’s a bit of a dance, but the rule is: the most specific definition wins.

Redirecting Traffic with aliases

So you’ve published a post at /blog/my-great-idea/. Then you realize the title—and therefore the slug—was terrible. You change it to /blog/my-brilliant-idea/. What happens to everyone who has the old URL bookmarked or linked to it? They get a 404. That’s a professional-grade oops.

This is where aliases save the day. It’s your built-in redirect system.

---
title: "My Brilliant Idea"
slug: "blog/my-brilliant-idea"
aliases:
  - "/blog/my-great-idea"
  - "/posts/old-url.html"
---

At build time, Eleventy will create HTML files at each of those alias paths that contain a simple <meta http-equiv="refresh"> tag to redirect the user to the canonical URL. It’s a client-side redirect, not a server-level 301, so it’s not quite as SEO-perfect, but it’s a damn sight better than a 404. It’s the “Hey, come over here instead!” of the web world.

Pitfall #2: Remember, these aliases need to be the full path, including the leading slash. And be judicious—if you change a URL, always add the old one as an alias. Your users will thank you.

The Nuclear Option: canonicalizeURLs

Here’s a fun quirk of the web: /blog/my-post/ and /blog/my-post/index.html are the same thing. But to a search engine or a browser, they’re technically different strings. This can lead to the dreaded “duplicate content” issue in SEO, where Google isn’t sure which URL is the real one.

Eleventy’s canonicalizeURLs feature is a toggle in your config that says, “Hey, sort this out for me.” When set to true, it forces all your generated URLs into their prettiest, most consistent form—almost always the one with the trailing slash.

// .eleventy.js
module.exports = function(eleventyConfig) {
  eleventyConfig.setBrowserSyncConfig({
    // ... other Browsersync options
  });

  return {
    // This is the important part
    canonicalizeURLs: true,
    dir: {
      input: "src",
      output: "_site"
    }
  };
};

With this enabled, if you somehow output a link to /blog/my-post/index.html, Eleventy’s built-in url filter will automatically convert it to /blog/my-post/. It’s a global clean-up operation. You should almost always turn this on. It’s like telling Eleventy to make its bed after it’s done coding—it just presents a tidier face to the world. The only reason not to is if you have some truly bizarre legacy URL structure to maintain, and if that’s the case, you have my condolences.