Guide Jekyll SEO

How to optimize Jekyll sites pour search — pourquoi its static HTML is crawler-friendly by par défaut, plus the jekyll-seo-tag and jekyll-sitemap plugins, permalinks, collections, robots.txt, and the GitHub Pages plugin whitelist.

Première publication : 26 juin 2026 · Dernière mise à jour : 3 août 2026 · Advanced
Langues
1 indice probant sur cette page

Jekyll outputs flat, static HTML — robots d’exploration obtenir complet content on the premier récupérer, with aucun JavaScript rendering delay. So the SEO fonctionner isn't fighting the framework; it's configuration. Install jekyll-seo-tag (title, description, canonical, Ouvrir Graph, Twitter Card, JSON-LD) and jekyll-sitemap (sitemap.xml), and définir url: in _config.yml or les deux produce broken output. Pick clean permalinks (/:title/, pas date-based) pour evergreen content. Créer robots.txt yourself — Jekyll won't. Watch two GitHub Pages traps: the plugin whitelist (seulement a fixed définir runs sans a GitHub Actions construire) and baseurl misconfiguration on project sites, qui breaks every URL canonique.

TL;DR — Jekyll emits flat static HTML at construire temps, so content is in the raw réponse on the premier explorer — aucun Web Rendering Service, aucun Wave 2 delay. The SEO fonctionner is configuration, pas architecture. Install jekyll-seo-tag (title, description, canonical, OG, Twitter Card, JSON-LD) and jekyll-sitemap (sitemap.xml); les deux exiger url: in _config.yml or ils produce broken output. Utiliser clean permalinks (/:title/) over date-based ones pour evergreen content. Two GitHub Pages traps dominate: the plugin whitelist (GitHub builds with --safe; non-whitelisted plugins besoin a GitHub Actions construire) and a manquant baseurl on project sites, qui breaks every generated URL canonique. GitHub Pages aussi pins spécifique plugin versions (3.10.0 core, older jekyll-seo-tag/jekyll-sitemap releases), pas simplement qui plugins run. Collections besoin output: true or they’re jamais rendered. Drafts, future-dated posts, and published: false documents are excluded from a normal construire, and --incremental is experimental — don’t utiliser it pour production deploys. And robots.txt n’est pas auto-generated — faire it yourself.

Pourquoi Jekyll’s static output is bon pour le SEO

Jekyll is a static site generator: it runs votre Markdown and Liquid templates via a construire step and emits finished HTML, une fois, avant quelconque requête. Que timing is the whole SEO advantage. Evidence for this claim Jekyll processes text and templates into static files during a build. Scope: Jekyll build architecture. Confidence: high · Verified: Jekyll documentation

Google’s pipeline is explorer → render → index, and rendering JavaScript is “a separate step” that sits in a queue — what people loosely call the “two-wave” traiter. Wave 1 récupère raw HTML and indexes text and liens immédiatement; Wave 2 queues lune page pour the Web Rendering Service to run JavaScript, “a few seconds to weeks” plus tard selon budget d’exploration. With Jekyll, Wave 1 déjà contient tout votre content — there’s aucun JavaScript requis to render the corps. Wave 1 = Wave 2. Aucun rendering delay, aucun rendering budget spent. Ce is the même argument que rend quelconque static site generator the lowest-risk architecture pour indexability.

The downstream benefits follow:

  • Faster TTFB. Pre-built fichiers served from a CDN (GitHub Pages sits behind Fastly; Netlify/Vercel have leur propre edges) mean aucun database requêtes and aucun server processing — bon pour LCP and the rest of Core Web Vitals.
  • Aucun requis JS payload pour content → meilleur FCP and LCP que a hydrating SPA.
  • Proper Code d’état HTTPs at the CDN couche, pas client-side error handling.

But none of que changements the fundamentals: static sites encore besoin meta tags, sitemaps, URL canoniques, données structurées, and bon content. Jekyll’s output is crawler-friendly; the metadata is on vous.

GitHub Pages and Jekyll SEO

The la plupart courant Jekyll deployment is GitHub Pages — push to a branch with Pages enabled and GitHub auto-builds le site. Que convenience comes with SEO-relevant constraints.

Project sites vs. utilisateur/org sites — l’URL decision

  • username.github.io/repo-name (a project site) lives on a shared subdomain with thousands of autre unrelated sites. It fonctionne fine pour docs, demos, and personal projects où l’URL itself doesn’t besoin to carry votre brand.
  • A custom domain (point a CNAME at GitHub Pages) puts le site on une URL vous contrôler, with HTTPS auto-provisioned by GitHub. Pour a site où the domain itself matters — a business site, a blog you’re building an audience autour — a custom domain from the commencer avoids a domain migration plus tard.

Si vous expect to déplacer to a custom domain eventually, définir it up early. Quelconque domain migration signifie 301 redirections (jekyll-redirect-from handles ce) and a period où inbound liens and quelconque accumulated signals point at the old URL — avoidable churn si vous pick the final domain up front plutôt que switching plus tard. Ce is a migration-planning argument, pas a claim que a github.io subdomain is penalized on its propre.

baseurl — the #1 canonical bug on project sites

Project sites live sous a subfolder (/repo-name/). Si vous don’t définir baseurl: /repo-name in _config.yml, every URL canonique jekyll-seo-tag generates — and votre lien internes — va be incorrect, manquant the subfolder. Ce is the unique la plupart courant canonical-URL bug on Jekyll project sites. (Utilisateur/org sites served at the domain root don’t besoin baseurl.)

The plugin whitelist

GitHub Pages runs Jekyll with the --safe flag and seulement permet a fixed définir of plugins. Evidence for this claim GitHub Pages builds Jekyll in safe mode and supports a documented set of plugins. Scope: GitHub Pages hosted builds. Confidence: high · Verified: GitHub Pages: Jekyll plugins The SEO-relevant whitelisted ones:

  • jekyll-seo-tag
  • jekyll-sitemap
  • jekyll-redirect-from ✓ (pour 301s quand vous modifier URLs)
  • jekyll-paginate

Pas whitelisted (and silently won’t run, or va error):

  • jekyll-last-modified-at — nécessaire pour accurate sitemap <lastmod> from fichier timestamps
  • Custom structured-data plugins
  • Anything dropped in a _plugins/ folder

It’s pas simplement qui plugins — it’s qui versions

GitHub Pages doesn’t seulement restrict qui plugins run; it pins the exact version of chaque un, and que construire environment itself is pinned to an older Jekyll. As of the dependency liste’s dernier mettre à jour, the hosted construire runs Jekyll 3.10.0 with jekyll-seo-tag 2.8.0, jekyll-sitemap 1.4.0, and jekyll-feed 0.17.0 — pendant que jekyllrb.com’s propre docs décrire the current upstream release, 4.4.1. A behavior documented in a plugin’s latest README isn’t guaranteed to exist in the version GitHub Pages en réalité runs; vérifier pages.github.com/versions.json pour the pinned version avant relying on a spécifique flag or output. A GitHub Actions construire sidesteps ce aussi — vous contrôler the Gemfile, so vous obtenir the versions vous pin, pas GitHub’s.

The GitHub Actions workaround

The fix pour the whitelist isn’t “add more gems” — it’s to arrêter letting GitHub do the construire. Run Jekyll yourself in CI (actions/jekyll-build-pages, or construire locally and push _site/ to the deploy branch with peaceiris/actions-gh-pages) and the whitelist ne … plus s’applique. Now quelconque plugin runs, and vous pick the Jekyll and plugin versions au lieu de inheriting GitHub’s pinned définir.

The jekyll-seo-tag plugin

Ce is the official, maintained plugin que covers la plupart of the core metadata Jekyll doesn’t ajouter on its propre. Exactly ce que it emits dépend on qui version vous have installed, votre _config.yml and front matter, and si votre layout en réalité calls {% seo %} — GitHub Pages, pour instance, pins a spécifique version plutôt que toujours shipping the latest release (plus on que ci-dessous). Vérifier the avancé usage guide pour votre version’s exact output, and confirmer ce que en réalité landed by grepping votre propre construit HTML plutôt que assuming it’s complet.

Install:

# Gemfile
gem 'jekyll-seo-tag'
# _config.yml
plugins:
  - jekyll-seo-tag
<!-- _layouts/default.html, before </head> -->
{% seo %}

Ce que it generates automatically:

  • <title> — page title with site nom appended (Page Title | Site Name)
  • <meta name="description"> — from description: front matter or le site description
  • <link rel="canonical"> — construit from site.url + page.url
  • Ouvrir Graph tags (og:title, og:description, og:url, og:site_name, og:image)
  • Twitter Card tags (twitter:card, twitter:title, twitter:description, twitter:creator, twitter:image)
  • JSON-LD données structurées (BlogPosting pour posts, WebSite pour the home page)
  • Pagination meta (suivant/prev URLs)

Requis _config.yml settings — sans ces the plugin produces broken output:

title: Your Site Title
description: Your site description
url: "https://yourdomain.com"   # CRITICAL — drives canonical URL generation
author:
  name: Patrick Stox
  twitter: patrickstox
  url: https://patrickstox.com  # author disambiguation
twitter:
  username: patrickstox
  card: summary_large_image

Per-page front matter overrides:

---
title: "Jekyll SEO Guide"
description: "How to optimize Jekyll sites for search engines."
image:
  path: /assets/jekyll-seo-og.png
  width: 1200
  height: 630
  alt: "Jekyll SEO diagram"
canonical_url: "https://example.com/jekyll-seo/"  # override if needed
robots: noindex   # per-page noindex
seo:
  type: BlogPosting          # schema.org type override
  date_modified: 2025-01-15  # dateModified override for JSON-LD
---

Suppression (quand votre layout déjà outputs its propre):

{% seo title=false %}      <!-- suppress the <title> -->
{% seo canonical=false %}  <!-- suppress the canonical link -->

Un gotcha worth flagging: nombreux minimal themes (notamment minima) ship sans jekyll-seo-tag wired in. Ajout the gem to votre Gemfile ne fait pashing unless the theme’s layout en réalité calls {% seo %}.

Sitemaps with jekyll-sitemap

Même install pattern (Gemfile + _config.yml). It generates a sitemaps.org- compliant sitemap.xml at /sitemap.xml on every construire.

It exige url: in _config.yml — sans it, sitemap entries have aucun domain.

Controlling <lastmod>, in priority order:

  1. last_modified_at: in front matter (meilleur — explicit contrôler)
  2. Post creation date (fallback — souvent incorrect pour evergreen content mis à jour plus tard)
  3. Filesystem modification date (nécessite the non-whitelisted jekyll-last-modified-at)

Meilleur pratique: ajouter last_modified_at: YYYY-MM-DD to every post, and bump it quand vous mettre à jour content — that’s the freshness signal recrawl prioritization leans on.

Excluding pages:

# Per-page front matter
sitemap: false

# Global pattern (in _config.yml)
defaults:
  - scope:
      path: "assets/**/*.pdf"
    values:
      sitemap: false

Permalinks are définir globally in _config.yml or overridden par page in front matter.

StylePatternSEO notes
date (par défaut)/:categories/:year/:month/:day/:title.htmlDate-heavy, fragile si the post date changements
pretty/:categories/:year/:month/:day/:title/Trailing slash, aucun .html
none/:categories/:title.htmlAucun date burial
Custom/:title/ or /:categories/:title/La plupart contrôler — recommended pour evergreen content

Recommended pour la plupart sites:

permalink: /:title/
# or
permalink: /:categories/:title/

Pourquoi éviter date-based URLs pour evergreen posts:

  • Modification a post’s date: front matter changements its URL → inbound liens break.
  • Deep hierarchy (/2019/03/14/post-title/) buries content pour aucun raison.
  • (Pour news and journalism, date URLs are fine and attendu — ce is a per-context appel, pas a universal rule.)

Collections besoin leur propre permalink config:

collections:
  case_studies:
    output: true
    permalink: /case-studies/:name/

A warning on modification patterns: une fois URLs are indexé, switching permalink styles exige 301 redirections (utiliser jekyll-redirect-from). Skip the redirections and vous break popularité des liens and generate 404s in Search Console.

Collections and SEO

Collections are Jekyll’s custom content types au-delà posts and pages — docs sections, portfolio items, team members, cas studies, FAQs. Two requirements faire or break leur SEO:

  1. output: true doit be définir. Sans it, collection documents are jamais rendered as individual HTML fichiers and therefore can’t be indexé. Ce is a silent indexation killer — le contenu exists in votre repo but jamais becomes a crawlable page.

    collections:
      docs:
        output: true          # REQUIRED for indexable pages
        permalink: /docs/:name/
  2. Chaque document nécessite front matter — même an vide --- block. Sans it, Jekyll treats the fichier as a binary static fichier: aucun Liquid processing, aucun metadata, aucun jekyll-seo-tag integration.

Remarque que collections aren’t inclus in RSS feeds (seulement posts are). Pour grand documentation sites, collections are usually the correct structure anyway.

Publishing states and incremental builds

Jekyll has several independent switches que contrôler ce que en réalité ends up in the construit site. Mixing les up produces soit pages que silently jamais obtenir construit, or draft/future content que ships to production by accident:

  • Drafts (_drafts/) are excluded from a normal construire entirely. Ils seulement apparaître quand vous run bundle exec jekyll serve --drafts (or build --drafts) locally — moving a post into _posts/ with a réel date is ce que en réalité publishes it.
  • Future-dated posts (a date: plus tard que the current temps) are excluded by par défaut. future: true in _config.yml, or the --future flag, inclut les — handy pour local preview, but leaving it on in a production config signifie scheduled posts go live the moment vous construire, pas on leur intended date.
  • published: false in front matter excludes a document from the construire regardless of its date — a separate switch from drafts and future posts, and facile to leave définir après testing une page vous meant to ship.
  • Incremental regeneration (--incremental) is documented by Jekyll as experimental, and it tracks a limited dependency graph — mainly inclut and layouts. Une page que iterates site.posts or autre collection données (a tag index, an archive, related-posts logic) peut go stale sous --incremental sans Jekyll detecting que the underlying posts modifié. Don’t run it as partie of a production deploy; utiliser a complet bundle exec jekyll build pour anything you’re pushing live.

Avant trusting quelconque of ces, construire sans the flags vous utiliser locally (--drafts, --future, --incremental) and confirmer the output in _site/ matches ce que vous intend to publish — the safest par défaut pour a deploy pipeline is a clean, complet, non-incremental construire.

Custom head partials with Liquid

Quand jekyll-seo-tag isn’t suffisant, vous construire votre propre _includes/head.html:

<head>
  <meta charset="UTF-8">
  <title>
    {% if page.title %}{{ page.title }} | {{ site.title }}
    {% else %}{{ site.title }}{% endif %}
  </title>
  <meta name="description" content="
    {%- if page.description -%}{{ page.description }}
    {%- elsif page.excerpt -%}{{ page.excerpt | strip_html | strip_newlines }}
    {%- else -%}{{ site.description }}
    {%- endif -%}">
  <link rel="canonical" href="{{ page.url | prepend: site.url }}">
  {% seo %}
</head>

Clé Liquid filters pour le SEO:

  • | strip_html — removes tags from an auto-excerpt (essential pour clean descriptions)
  • | strip_newlines — removes line breaks from excerpts
  • | truncate: 160 — an optional template-side character cap, pas a Google limite. Préférer an authored, page-specific description and preview the rendered snippet; fit varies by requête, device, language, and script.
  • | prepend: site.url — builds absolute URLs pour canonical and OG tags
  • | date_to_xmlschema — ISO 8601 dates pour JSON-LD datePublished/dateModified
  • | default: fallback — fallback quand a variable is nil

Custom JSON-LD au-delà ce que jekyll-seo-tag emits:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": {{ page.title | jsonify }},
  "datePublished": "{{ page.date | date_to_xmlschema }}",
  "dateModified": "{{ page.last_modified_at | default: page.date | date_to_xmlschema }}",
  "author": {
    "@type": "Person",
    "name": "{{ page.author.name | default: site.author.name }}",
    "url": "{{ page.author.url | default: site.author.url }}"
  }
}
</script>

robots.txt n’est pas auto-generated

Jekyll fait pas créer robots.txt. Vous faire it yourself in le site root. Inclure an vide front-matter block so Liquid processes the fichier (so {{ site.url }} resolves):

---
---
User-agent: *
Allow: /

Sitemap: {{ site.url }}/sitemap.xml

Sans ce, there’s aucun sitemap référence in robots.txt — and some robots d’exploration utiliser que as a discovery mechanism. (robots.txt contrôle exploration, pas indexation — pour the complet picture voir exploration.)

Courant Jekyll SEO myths

“Jekyll handles SEO automatically.” The static output is crawler-friendly, but metadata exige explicit setup. Par défaut themes souvent ship with aucun SEO tags at tout.

“GitHub Pages is fine for SEO — it’s free.” GitHub Pages itself is fine — the static output is crawler-friendly regardless of host. The catches are the plugin whitelist (and the spécifique versions it pins, pas simplement qui plugins) and, si votre brand dépend on l’URL, deciding on a custom domain avant vous construire an audience on github.io.

“Static sites don’t need sitemaps.” Google peut trouver pages via liens, but a sitemap speeds discovery and carries <lastmod> signals. jekyll-sitemap rend it trivial.

“Jekyll is dead.” It’s in mature maintenance mode — v4.4,1 shipped in January 2025. Minimal nouveau fonctionnalités, but maintained and secure, and GitHub Pages va prise en charge it indefinitely. Pour simple blogs and docs, it’s a solid, boring choice.

“I can use any plugin on GitHub Pages.” Aucun — --safe plus a fixed whitelist. The fix is GitHub Actions, pas plus gems.

Où ce fits

Jekyll is un of the six generators in the static site generators cluster — the GitHub Pages par défaut and the un la plupart developers meet premier. Pour the broader context on how Google renders JavaScript and quand vous en réalité besoin it, voir the JavaScript SEO hub. The build-freshness discipline que s’applique to every SSG — a static site is seulement as current as its dernier construire — s’applique to Jekyll aussi: edits don’t reach moteur de recherches jusqu’à vous rebuild and redeploy.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.