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.

Your permalink structure is one of the most consequential SEO decisions you make for a Jekyll site. URLs are visible in search results, shared on social media, cited in external links, and remembered by returning visitors. A well-designed permalink structure is clean, descriptive, and stable — changing it after your site has inbound links or search rankings is expensive.

The most SEO-friendly permalink format for blog posts is the title-only slug: /blog/jekyll-seo-guide/ rather than /blog/2025/12/16/jekyll-seo-guide/. Title-based slugs communicate the topic of the page to both users and search engines, they do not expose the publication date (which can make older content feel stale), and they are significantly shorter which makes them easier to share and remember. In _config.yml, set this with permalink: /blog/:title/.

Date-based URLs have legitimate uses for news sites and blogs where publication date is a meaningful signal — a political news site where “today’s coverage” matters is better served by /news/2025/12/16/story-title/ than by /news/story-title/. For evergreen technical content, tutorial sites, and theme marketplaces, date-based URLs are usually a liability rather than an asset.

Include the primary keyword in the slug where it appears naturally. A post titled “How to Install a Jekyll Theme on macOS” has the slug how-to-install-a-jekyll-theme-on-macos by default — which is appropriate. Do not stuff additional keywords into slugs; the post title’s natural keywords are sufficient. Very long slugs (over 60 characters) can be shortened by removing stop words: how-to-install-a-jekyll-theme-on-macos becomes install-jekyll-theme-macos without losing keyword meaning.

When you change a permalink — whether for a single post or by changing the global permalink: format — you must redirect the old URL to the new one to preserve inbound links and avoid 404 errors. Each 404 on a URL that previously had inbound links is a lost backlink; over time, accumulating 404s from permalink changes can meaningfully reduce your domain authority.

For Netlify-hosted Jekyll sites, the _redirects file in your repository root handles redirects at the CDN level with zero latency: add old/url/ new/url/ 301 for a permanent redirect. For Cloudflare Pages, the same syntax works. For GitHub Pages, a jekyll-redirect-from plugin adds a redirect HTML page at the old URL that meta-refreshes to the new one — less clean than a true server-side redirect, but functional.

Set up Google Search Console and monitor the Coverage report after any permalink change. Search Console shows 404 errors with the inbound links that were trying to reach them — a direct map of which old URLs need redirects. Resolving these 404s promptly, within days of the permalink change, minimises the time during which inbound link authority is not being passed to the new URL.

Canonical URLs and duplicate content

Jekyll sites can inadvertently create duplicate content if multiple URLs serve the same or similar content. A blog post accessible at both /blog/post-title/ and /blog/post-title (with and without trailing slash) is a duplicate content risk if both URLs are indexed. Configure your Jekyll site to consistently redirect one format to the other — most hosts handle this with a redirect rule, and the jekyll-seo-tag plugin’s canonical_url output in the <head> tells search engines which version is authoritative.

Paginated archives are another potential duplicate content source. The first page of your blog at /blog/ and the paginated version at /blog/page/1/ may serve identical content. Configure your paginator to only generate /blog/page/2/ onwards, making /blog/ itself the first page — this is the default behaviour of jekyll-paginate-v2 but worth verifying in your specific theme.

Tag and category pages can create near-duplicate content when a single post appears on multiple archive pages with the same excerpt. Use noindex carefully — you generally want category and tag pages indexed so they rank for their topic keywords — but ensure your theme generates distinct, topic-appropriate titles and descriptions for each archive page rather than repeating generic metadata.

After configuring your permalink structure, test it before publishing extensively. Build your site locally with bundle exec jekyll build and browse the _site/ directory to verify that posts are being generated at the expected paths. Check a few edge cases: a post title with special characters (apostrophes, colons, question marks) that might produce unexpected slugs; posts in subdirectories of _posts/; and any posts with custom permalink: in their front matter that should override the global setting.

Use the jekyll-link-checker gem or an HTML proofer tool to scan your built site for internal broken links before deploying. Internal 404s from misconfigured links or incorrect link tags are easy to introduce and easy to miss without automated checking. A clean link check before each major deployment — especially after changing the permalink structure — is a low-cost insurance policy against broken internal navigation.

Permalink design is a low-visibility decision with long-lasting consequences — once you publish URLs, changing them breaks inbound links, social shares, and search rankings built up over time. Spend an hour deciding on your permalink structure before you publish your first post rather than months later dealing with the fallout of a necessary restructure. A date-free, category-organised slug like /blog/jekyll-collections-guide/ is the structure most likely to remain appropriate as your site grows and your content strategy evolves.

Share LinkedIn