Home Blog Jekyll Permalinks and URL Structure: The Complete Guide
Tutorial

Jekyll Permalinks and URL Structure: The Complete Guide

How Jekyll builds URLs — permalink patterns, built-in styles, custom permalinks for posts, pages, and collections, and best practices for SEO-friendly URLs.

Jekyll Permalinks and URL Structure: The Complete Guide

Jekyll gives you precise control over the URL structure of every page on your site. Understanding how permalinks work is important for clean URLs, SEO, and ensuring internal links stay consistent when you reorganise content.

How Jekyll builds URLs by default

By default, Jekyll mirrors your file structure. A file at _posts/2026-08-07-my-post.md generates a URL like /2026/08/07/my-post/. A page at about.md generates /about/.

But the default is rarely what you want for production. The permalink setting in _config.yml (and in individual file front matter) lets you control this precisely.

Set a global permalink pattern in _config.yml:

permalink: /blog/:title/

This tells Jekyll: every post’s URL should be /blog/ followed by the post’s title-slug.

Jekyll ships with several named permalink patterns:

permalink: date      # /year/month/day/title.html
permalink: pretty    # /year/month/day/title/  (trailing slash, no .html)
permalink: ordinal   # /year/ordinal/title.html
permalink: weekdate  # /year/week/short_day/title/
permalink: none      # /title.html

Most sites use pretty or a custom pattern. date (the historical default) produces cluttered URLs. none creates flat URLs with .html extensions.

Build custom patterns using these placeholders:

Placeholder Value Example
:year 4-digit year 2026
:month 2-digit month 08
:day 2-digit day 07
:hour 2-digit hour (24h) 14
:minute 2-digit minute 30
:second 2-digit second 00
:title Slugified title my-post-title
:slug Slug from front matter (fallback to title) custom-slug
:categories Categories joined by / tutorial/jekyll
:name Filename without date and extension my-post-title
:path Path relative to site root _posts/my-post.md
:output_ext Output file extension .html

Common custom patterns

# Clean blog URL — most common for blog sites
permalink: /blog/:title/

# With date — good for news sites
permalink: /:year/:month/:title/

# Category-based
permalink: /:categories/:title/

# Flat — no nesting
permalink: /:title/

# With date and category
permalink: /:categories/:year/:month/:day/:title/
permalink: /blog/:title/

This gives you:

  • Clean, readable URLs: /blog/jekyll-front-matter-guide/
  • No date in the URL (posts stay relevant even when old)
  • Consistent /blog/ prefix separating blog content from pages
  • Easy to remember and share

Avoid including dates in permalinks unless you publish time-sensitive content (news, changelogs) where date context adds value.

Override the global setting in any file’s front matter:

---
layout: page
title: "About"
permalink: /about/
---
---
layout: post
title: "My Special Post"
permalink: /featured/my-special-post/
---

The front matter permalink always wins over the global setting in _config.yml.

Pages (in _pages/ or the root directory) use the same permalink front matter key:

---
layout: page
title: "Browse Themes"
permalink: /themes/
---
---
layout: page
title: "Submit a Theme"
permalink: /submit/
---

Without a permalink, a page at _pages/about.md generates /about (no trailing slash). Set permalink: /about/ explicitly for consistency.

Collections get their permalink pattern in _config.yml under the collection definition:

collections:
  themes:
    output: true
    permalink: /themes/:name/
  authors:
    output: true
    permalink: /authors/:name/

For a file _themes/minimal-mistakes.md, this generates /themes/minimal-mistakes/.

Available placeholders for collections: :name (filename without extension), :path, :output_ext, :title, :categories.

# Custom collection permalink using title from front matter
collections:
  themes:
    output: true
    permalink: /themes/:title/

The :title placeholder in detail

:title uses the post or page title, converted to a URL-safe slug:

  • Lowercase
  • Spaces replaced with hyphens
  • Special characters removed
  • Accented characters transliterated (é → e)
Title: "Jekyll Front Matter: The Complete Guide!"
:title → "jekyll-front-matter-the-complete-guide"

If you want a different slug than the auto-generated one, set slug in front matter:

---
title: "Jekyll Front Matter: The Complete Guide!"
slug: jekyll-front-matter-guide
permalink: /blog/:slug/
---

This gives /blog/jekyll-front-matter-guide/ instead of the long auto-generated version.

The :categories placeholder

If posts have categories, :categories generates a nested URL:

---
categories: [Tutorial, Jekyll]
permalink: /:categories/:title/
---

Generates: /tutorial/jekyll/my-post/

If a post has no categories, :categories is omitted from the URL (Jekyll does not include the empty slash).

Warning: Using :categories in your permalink means changing a post’s category changes its URL — which breaks links and SEO. Avoid :categories in permalinks for blog posts. Use it only for intentional category-based URL structures.

Trailing slashes

Jekyll generates index.html inside a folder for trailing-slash URLs:

permalink: /about/
→ _site/about/index.html
→ served at https://example.com/about/

Without a trailing slash:

permalink: /about
→ _site/about.html
→ served at https://example.com/about

Use trailing slashes consistently. Mixing /about/ and /contact causes inconsistency and potential duplicate content. Most modern Jekyll sites use trailing slashes.

If you change a permalink on an existing post, the old URL breaks. Redirect it using jekyll-redirect-from:

# Gemfile
gem "jekyll-redirect-from"
---
layout: post
title: "My Post"
permalink: /blog/my-new-url/
redirect_from:
  - /2026/08/07/my-old-url/
  - /blog/my-old-url/
---

Jekyll generates redirect pages at the old URLs pointing to the new one. Essential for maintaining SEO when restructuring content.

Checking generated URLs

To see what URL Jekyll generates for each file, run a build and check the _site/ directory structure — it mirrors your URL structure exactly:

bundle exec jekyll build
find _site -name "index.html" | head -20

Or use jekyll serve and browse to check each URL manually.

When linking between pages internally, use the {% link %} or {% post_url %} tag instead of hardcoding URLs — they account for baseurl and raise a build error if the target file does not exist:


<a href="{% link _posts/2026-08-07-my-post.md %}">My Post</a>
<a href="{% post_url 2026-08-07-my-post %}">My Post</a>

Both resolve to the post’s actual URL, whatever the permalink setting.

Keyword in URL: Shorter URLs that include the post’s main keyword perform slightly better. /blog/jekyll-permalinks/ is better than /blog/jekyll-permalinks-and-url-structure-the-complete-guide-2026/.

Avoid dates unless meaningful: /blog/2026/08/07/my-post/ makes content look dated. /blog/my-post/ is evergreen.

Be consistent: Changing URL structure after publishing harms SEO even with redirects. Choose a structure you can live with long-term before publishing.

Use hyphens, not underscores: Google treats hyphens as word separators in URLs. my-post is two words; my_post is one. Use hyphens.

Short is better: The shorter and more descriptive the URL, the better. Remove stop words (the, a, an, and, or) from slugs when they add no meaning.

Here is a solid permalink configuration for a Jekyll blog/marketplace:

# _config.yml

# Blog posts
permalink: /blog/:title/

# Collections
collections:
  themes:
    output: true
    permalink: /themes/:name/
  authors:
    output: true
    permalink: /authors/:name/

Pages set their own permalink in front matter:

# _pages/themes.md
permalink: /themes/

# _pages/about.md
permalink: /about/

# _pages/blog.md (blog index)
permalink: /blog/

This gives a clean, consistent URL structure where /blog/ contains posts, /themes/ contains theme pages, and top-level paths handle static pages.

Share LinkedIn