Guide Jekyll SEO

«How to optimize Jekyll sites for search — why its static HTML is crawler-friendly by default, plus the jekyll-seo-tag and jekyll-sitemap plugins, permalinks, collections, robots.txt, and the GitHub Pages plugin whitelist.» _(Traduction)_ (Résumé en français du champ description : le texte source est conservé pour relecture francophone.)

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

Jekyll outputs flat, static HTML — robots d’exploration obtenir complet contenu on le premier récupérer, avec aucun JavaScript rendu delay. Donc le SEO fonctionner n’est pas fighting le framework; c’est configuration. Install jekyll-seo-balise (titre, description, canonical, Ouvrir Graph, Twitter Card, JSON-LD) et jekyll-sitemap (sitemap.xml), et définir url: dans _config.yml or les deux produce broken sortie. Pick clean permalinks (/:titre/, pas date-based) pour evergreen contenu. Créer robots.txt yourself — Jekyll ne va pas. Watch deux GitHub Pages traps: le plugin whitelist (seulement a fixed définir exécute sans a GitHub Actions construire) et baseurl misconfiguration on project sites, qui breaks chaque URL canonique.

TL;DR — Jekyll emits flat static HTML à construire temps, donc contenu est dans le raw réponse on le premier explorer — aucun Web Rendu Service, aucun Wave 2 delay. Le SEO fonctionner est configuration, pas architecture. Install jekyll-seo-tag (titre, description, canonical, OG, Twitter Card, JSON-LD) et jekyll-sitemap (sitemap.xml); les deux exiger url: dans _config.yml or ils produce broken sortie. Utiliser clean permalinks (/:title/) over date-based ones pour evergreen contenu. Deux GitHub Pages traps dominate: le plugin whitelist (GitHub construit avec --safe; non-whitelisted plugins besoin a GitHub Actions construire) et a manquant baseurl on project sites, qui breaks chaque 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 exécuter. Collections besoin output: true or ils sont jamais rendu. Drafts, future-dated posts, et published: false documents sont excluded depuis a normal construire, et --incremental est experimental — ne pas utiliser il pour production deploys. Et robots.txt n’est pas auto-generated — faire il yourself.

Pourquoi Jekyll’s static output is bon pour le SEO

«Jekyll is a static site generator: it runs your Markdown and Liquid templates through a build step and emits finished HTML, once, before any request. That timing is the whole SEO advantage. Preuve à l’appui de cette affirmation Jekyll processes text and templates into static files during a build. Portée : Jekyll build architecture. Niveau de confiance : élevé · Vérifié : Jekyll documentation » (Traduction) (Résumé en français de la section dix-neuf, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«Google’s pipeline is crawl → render → index, and rendering JavaScript is “a separate step” that sits in a queue — what people loosely call the “two-wave” process. Wave 1 fetches raw HTML and indexes text and links immediately; Wave 2 queues the page for the Web Rendering Service to run JavaScript, “a few seconds to weeks” later depending on crawl budget. With Jekyll, Wave 1 already contains all your content — there’s no JavaScript required to render the body. Wave 1 = Wave 2. No rendering delay, no rendering budget spent. This is the same argument that makes any static site generator the lowest-risk architecture for indexability. » (Traduction) (Résumé en français de la section vingt, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«The downstream benefits follow: » (Traduction) (Résumé en français de la section vingt et un, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«- Faster TTFB. Pre-built files served from a CDN (GitHub Pages sits behind Fastly; Netlify/Vercel have their own edges) mean no database queries and no server processing — good for LCP and the rest of Core Web Vitals. » (Traduction) (Résumé en français de la section vingt-deux, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.) «- No required JS payload for content → better FCP and LCP than a hydrating SPA. » (Traduction) (Résumé en français de la section vingt-deux, sous-partie deux : le texte source est conservé pour vérification lors de la relecture francophone.) «- Proper HTTP status codes at the CDN layer, not client-side error handling. » (Traduction) (Résumé en français de la section vingt-deux, sous-partie trois : le texte source est conservé pour vérification lors de la relecture francophone.)

«But none of that changes the fundamentals: static sites still need meta tags, sitemaps, canonical URLs, structured data, and good content. Jekyll’s output is crawler-friendly; the metadata is on you. » (Traduction) (Résumé en français de la section vingt-trois, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

GitHub Pages et Jekyll SEO

Le la plupart courant Jekyll deployment est GitHub Pages — push à a branch avec Pages enabled et GitHub auto-construit le site. Que convenience comes avec SEO-pertinent constraints.

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

  • username.github.io/repo-name (a project site) lives on a shared subdomain avec thousands de autre unrelated sites. Il fonctionne fine pour docs, demos, et personal projects où l’URL itself ne fait pas besoin à carry votre brand.
  • A custom domain (point a CNAME à GitHub Pages) puts le site on une URL vous contrôler, avec HTTPS auto-provisioned par GitHub. Pour a site où le domain itself matters — a entreprise site, a blog vous êtes building an public autour — a custom domain depuis le commencer avoids a domain migration plus tard.

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

baseurl — le #1 canonical bug on project sites

«Project sites live under a subfolder (/repo-name/). If you don’t set baseurl: /repo-name in _config.yml, every canonical URL jekyll-seo-tag generates — and your internal links — will be wrong, missing the subfolder. This is the single most common canonical-URL bug on Jekyll project sites. (User/org sites served at the domain root don’t need baseurl.) » (Traduction) (Résumé en français de la section trente, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«### The plugin whitelist » (Traduction) (Résumé en français de la section trente et un, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«GitHub Pages runs Jekyll with the --safe flag and only allows a fixed set of plugins. Preuve à l’appui de cette affirmation GitHub Pages builds Jekyll in safe mode and supports a documented set of plugins. Portée : GitHub Pages hosted builds. Niveau de confiance : élevé · Vérifié : GitHub Pages: Jekyll plugins The SEO-relevant whitelisted ones: » (Traduction) (Résumé en français de la section trente-deux, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«- jekyll-seo-tag ✓ » (Traduction) (Résumé en français de la section trente-trois, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.) «- jekyll-sitemap ✓ » (Traduction) (Résumé en français de la section trente-trois, sous-partie deux : le texte source est conservé pour vérification lors de la relecture francophone.) «- jekyll-redirect-from ✓ (for 301s when you change URLs) » (Traduction) (Résumé en français de la section trente-trois, sous-partie trois : le texte source est conservé pour vérification lors de la relecture francophone.) «- jekyll-paginate ✓ » (Traduction) (Résumé en français de la section trente-trois, sous-partie quatre : le texte source est conservé pour vérification lors de la relecture francophone.)

Pas whitelisted (et silently ne va pas exécuter, or va erreur):

  • 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 not just which plugins — it’s which versions » (Traduction) (Résumé en français de la section trente-six, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«GitHub Pages doesn’t only restrict which plugins run; it pins the exact version of each one, and that build environment itself is pinned to an older Jekyll. As of the dependency list’s last update, the hosted build runs Jekyll 3.10.0 with jekyll-seo-tag 2.8.0, jekyll-sitemap 1.4.0, and jekyll-feed 0.17.0 — while jekyllrb.com’s own docs describe 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 actually runs; check pages.github.com/versions.json for the pinned version before relying on a specific flag or output. A GitHub Actions build sidesteps this too — you control the Gemfile, so you get the versions you pin, not GitHub’s. » (Traduction) (Résumé en français de la section trente-sept, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«### The GitHub Actions workaround » (Traduction) (Résumé en français de la section trente-huit, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«The fix for the whitelist isn’t “add more gems” — it’s to stop letting GitHub do the build. Run Jekyll yourself in CI (actions/jekyll-build-pages, or build locally and push _site/ to the deploy branch with peaceiris/actions-gh-pages) and the whitelist no longer applies. Now any plugin runs, and you pick the Jekyll and plugin versions instead of inheriting GitHub’s pinned set. » (Traduction) (Résumé en français de la section trente-neuf, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

The jekyll-seo-tag plugin

Ce est le official, maintained plugin que covers la plupart de le core metadata Jekyll ne fait pas ajouter on son propre. Exactly ce que il emits dépend on qui version vous ont installed, votre _config.yml et front matter, et si votre layout en réalité calls {% seo %} — GitHub Pages, pour instance, pins a spécifique version plutôt que toujours shipping le latest release (plus on que ci-dessous). Vérifier le avancé usage guide pour votre version’s exact sortie, et confirmer ce que en réalité landed par grepping votre propre construit HTML plutôt que assuming c’est 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 titre avec site nom appended (Page Title | Site Name)
  • <meta name="description"> — depuis description: front matter or le site description
  • <link rel="canonical"> — construit depuis site.url + page.url
  • Ouvrir Graph balises (og:title, og:description, og:url, og:site_name, og:image)
  • Twitter Card balises (twitter:card, twitter:title, twitter:description, twitter:creator, twitter:image)
  • JSON-LD données structurées (BlogPosting pour posts, WebSite pour le page d’accueil)
  • Pagination meta (suivant/prev URLs)

Requis _config.yml paramètres — sans ces le plugin produces broken sortie:

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

«Same install pattern (Gemfile + _config.yml). It generates a sitemaps.org- compliant sitemap.xml at /sitemap.xml on every build. » (Traduction) (Résumé en français de la section cinquante-six, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«It requires url: in _config.yml — without it, sitemap entries have no domain. » (Traduction) (Résumé en français de la section cinquante-sept, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«Controlling <lastmod>, in priority order: » (Traduction) (Résumé en français de la section cinquante-huit, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«1. last_modified_at: in front matter (best — explicit control) 2. Post creation date (fallback — often wrong for evergreen content updated later) 3. Filesystem modification date (needs the non-whitelisted jekyll-last-modified-at) » (Traduction) (Résumé en français de la section cinquante-neuf, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«Best practice: add last_modified_at: YYYY-MM-DD to every post, and bump it when you update content — that’s the freshness signal recrawl prioritization leans on. » (Traduction) (Résumé en français de la section soixante, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«Excluding pages: » (Traduction) (Résumé en français de la section soixante et un, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

# Per-page front matter
sitemap: false

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

«## Permalink configuration » (Traduction) (Résumé en français de la section soixante-trois, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«Permalinks are set globally in _config.yml or overridden per page in front matter. » (Traduction) (Résumé en français de la section soixante-quatre, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«| Style | Pattern | SEO notes | |-------|---------|-----------| | date (default) | /:categories/:year/:month/:day/:title.html | Date-heavy, fragile if the post date changes | | pretty | /:categories/:year/:month/:day/:title/ | Trailing slash, no .html | | none | /:categories/:title.html | No date burial | | Custom | /:title/ or /:categories/:title/ | Most control — recommended for evergreen content | » (Traduction) (Résumé en français de la section soixante-cinq, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«Recommended for most sites: » (Traduction) (Résumé en français de la section soixante-six, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

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

«Why avoid date-based URLs for evergreen posts: » (Traduction) (Résumé en français de la section soixante-huit, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«- Changing a post’s date: front matter changes its URL → inbound links break. » (Traduction) (Résumé en français de la section soixante-neuf, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.) «- Deep hierarchy (/2019/03/14/post-title/) buries content for no reason. » (Traduction) (Résumé en français de la section soixante-neuf, sous-partie deux : le texte source est conservé pour vérification lors de la relecture francophone.) «- (For news and journalism, date URLs are fine and expected — this is a per-context call, not a universal rule.) » (Traduction) (Résumé en français de la section soixante-neuf, sous-partie trois : le texte source est conservé pour vérification lors de la relecture francophone.)

«Collections need their own permalink config: » (Traduction) (Résumé en français de la section soixante-dix, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

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

«A warning on changing patterns: once URLs are indexed, switching permalink styles requires 301 redirects (use jekyll-redirect-from). Skip the redirects and you break link equity and generate 404s in Search Console. » (Traduction) (Résumé en français de la section soixante-douze, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«## Collections and SEO » (Traduction) (Résumé en français de la section soixante-treize, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

«Collections are Jekyll’s custom content types beyond posts and pages — docs sections, portfolio items, team members, case studies, FAQs. Two requirements make or break their SEO: » (Traduction) (Résumé en français de la section soixante-quatorze, sous-partie un : le texte source est conservé pour vérification lors de la relecture francophone.)

  1. output: true doit être définir. Sans il, collection documents sont jamais rendu as individual HTML fichiers et therefore ne peut pas être indexé. Ce est a silent indexation killer — le contenu exists dans votre repo mais 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 ne sont pas inclus dans RSS feeds (seulement posts sont). Pour grand documentation sites, collections sont généralement le correct structure anyway.

Publishing states et incremental construit

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/) sont excluded depuis a normal construire entirely. Ils seulement apparaître quand vous exécuter bundle exec jekyll serve --drafts (or build --drafts) locally — moving a post dans _posts/ avec a réel date est ce que en réalité publishes il.
  • Future-dated posts (a date: plus tard que le actuel temps) sont excluded par par défaut. future: true dans _config.yml, or le --future flag, inclut les — handy pour local preview, mais leaving il on dans a production config signifie scheduled posts go actif le moment vous construire, pas on leur prévu date.
  • published: false dans front matter excludes a document depuis le construire regardless de son date — a séparé switch depuis drafts et future posts, et facile à laisser définir après testing une page vous meant à ship.
  • Incremental regeneration (--incremental) est documented par Jekyll as experimental, et il tracks a limited dependency graph — mainly inclut et layouts. Une page que iterates site.posts or autre collection données (a balise indexer, an archive, related-posts logic) peut go stale sous --incremental sans Jekyll detecting que le underlying posts modifié. ne pas exécuter il as partie de a production deploy; utiliser a complet bundle exec jekyll build pour n’importe quoi vous êtes pushing actif.

Avant trusting quelconque de ces, construire sans le flags vous utiliser locally (--drafts, --future, --incremental) et confirmer le sortie dans _site/ matches ce que vous intend à publish — le safest par défaut pour a deploy pipeline est a clean, complet, non-incremental construire.

Custom head partials avec 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 — supprime balises depuis an auto-excerpt (essential pour clean descriptions)
  • | strip_newlines — supprime line breaks depuis excerpts
  • | truncate: 160 — an optional template-side character cap, pas a Google limite. Préférer an authored, page-spécifique description et preview le rendu extrait; fit varies par requête, device, langue, et script.
  • | prepend: site.url — construit absolute URLs pour canonical et OG balises
  • | date_to_xmlschema — ISO 8601 dates pour JSON-LD datePublished/dateModified
  • | default: fallback — fallback quand a variable est 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, il y a aucun sitemap référence dans robots.txt — et some robots d’exploration utiliser que as a discovery mechanism. (robots.txt contrôle exploration, pas indexation — pour le complet picture voir exploration.)

Courant Jekyll SEO myths

“Jekyll handles SEO automatiquement.” Le static sortie est robot-friendly, mais metadata exige explicit setup. Par défaut themes souvent ship avec aucun SEO balises à tout.

“GitHub Pages est fine pour SEO — c’est gratuit.” GitHub Pages itself est fine — le static sortie est robot-friendly regardless de host. Le catches sont le plugin whitelist (et le spécifique versions il pins, pas simplement qui plugins) et, si votre brand dépend on l’URL, deciding on a custom domain avant vous construire an public on github.io.

“Static sites don’t need sitemaps.” Google peut trouver pages via liens, mais a sitemap speeds discovery et carries <lastmod> signaux. jekyll-sitemap rend il trivial.

“Jekyll is dead.” c’est dans mature maintenance mode — v4.4,1 shipped dans January 2025. Minimal nouveau fonctionnalités, mais maintained et secure, et GitHub Pages va prise en charge il indefinitely. Pour simple blogs et docs, c’est 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 est un de le six generators dans le static site generators cluster — le GitHub Pages par défaut et le un la plupart developers meet premier. Pour le broader context on comment Google renders JavaScript et quand vous en réalité besoin il, voir le JavaScript SEO hub. Le construire-freshness discipline que s’applique à chaque SSG — a static site est seulement as actuel as son dernier construire — s’applique à Jekyll aussi: edits ne pas reach moteur de recherches jusqu’à vous rebuild et redeploy.

Ajouter une note d’expert

Épingler une citation d’expert

Nouvelle personne ? Créez son profil non revendiqué à /admin/experts/ → Épingler une citation d’expert d’abord.