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.
1 evidence signal on this page
- Linked source datadependency list
Jekyll outputs flat, static HTML — crawlers get complete content on the first fetch, with no JavaScript rendering delay. So the SEO work isn't fighting the framework; it's configuration. Install jekyll-seo-tag (title, description, canonical, Open Graph, Twitter Card, JSON-LD) and jekyll-sitemap (sitemap.xml), and set url: in _config.yml or both produce broken output. Pick clean permalinks (/:title/, not date-based) for evergreen content. Create robots.txt yourself — Jekyll won't. Watch two GitHub Pages traps: the plugin whitelist (only a fixed set runs without a GitHub Actions build) and baseurl misconfiguration on project sites, which breaks every canonical URL.
TL;DR — Jekyll builds your whole site into plain HTML files ahead of time, so Google sees your content on the first visit — no waiting for JavaScript. That’s a great starting point for SEO. The work is in the setup: install two plugins (
jekyll-seo-tagfor meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored.,jekyll-sitemapfor your sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing.), set your site URL in the config, pick clean URLs, and add arobots.txtyourself.
What Jekyll is
Jekyll is a static site generatorA build tool that pre-renders every page into static HTML files at deploy time, so the server delivers complete HTML without executing code per request — eliminating JavaScript rendering delays for search engine crawlers. — a tool that turns your content (written in
Markdown) and templates into finished HTML files before anyone visits. It’s
written in Ruby, and it’s the engine behind GitHub Pages, so if you’ve ever
pushed a project’s docs or a personal blog to a github.io address, you’ve
probably used Jekyll without thinking about it. Evidence for this claim Jekyll transforms source content and templates into a static website and is supported by GitHub Pages. Scope: Jekyll and GitHub Pages. Confidence: high · Verified: Jekyll documentation GitHub Pages: Jekyll
The important part for SEO: because Jekyll produces plain HTML files, a search engine gets your complete content the moment it fetches the page. There’s no JavaScript that has to run first. That removes one entire failure mode from the table — it doesn’t guarantee indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. or rankings by itself, since that still depends on your content, configuration, and the metadata covered below.
What Jekyll does not do for you
Here’s the catch beginners hit: Jekyll is crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index.-friendly, but it isn’t “SEO-friendly” out of the box. A default Jekyll theme often ships with no title tags, meta descriptionsThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through., or structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding. at all. You have to add those. The good news is two official plugins do almost all of it:
jekyll-seo-tag— adds your title tagThe title tag is the HTML title element in a page's head that specifies the document's title. It's the primary source for the SERP title link and a confirmed light ranking factor — but since August 2021 Google doesn't always show it verbatim., meta descriptionThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through., canonical URLHow search engines pick one canonical URL among duplicates and consolidate signals onto it., and social-sharing tags automatically.jekyll-sitemap— builds yoursitemap.xmlautomatically. Evidence for this claim The jekyll-seo-tag and jekyll-sitemap plugins generate SEO tags and sitemap files for Jekyll sites. Scope: Named Jekyll plugins. Confidence: high · Verified: Jekyll SEO Tag Jekyll Sitemap
The five things to get right
- Install
jekyll-seo-tagand put{% seo %}in your layout’s<head>. - Install
jekyll-sitemapso search engines get a list of your pages. - Set
url:in_config.ymlto your real domain — both plugins need it, or your canonical URLs and sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. point atlocalhostand break. - Use a custom domain, not the free
username.github.io/repoaddress, for any site where ranking matters. - Add a
robots.txtyourself — Jekyll doesn’t create one.
The GitHub Pages surprise
If you host on GitHub Pages, you can’t just install any plugin you want. GitHub
runs Jekyll in a locked-down mode that only allows a short list of plugins.
Thankfully jekyll-seo-tag and jekyll-sitemap are both on that list — so the
essentials work. For anything fancier you’d need a different build setup (covered
in the Advanced tab).
Want the full version — the plugin whitelist, the baseurl bug that breaks
canonical URLs, permalinks, collections, and custom structured data? Switch to the
Advanced tab.
TL;DR — Jekyll emits flat static HTML at build time, so content is in the raw response on the first crawl — no Web Rendering ServiceTurning HTML, CSS, and JavaScript into the final visual page and DOM., no Wave 2 delay. The SEO work is configuration, not architecture. Install
jekyll-seo-tag(title, description, canonical, OG, Twitter Card, JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal.) andjekyll-sitemap(sitemap.xml); both requireurl:in_config.ymlor they produce broken output. Use clean permalinks (/:title/) over date-based ones for evergreen content. Two GitHub Pages traps dominate: the plugin whitelist (GitHub builds with--safe; non-whitelisted plugins need a GitHub Actions build) and a missingbaseurlon project sites, which breaks every generated canonical URLHow search engines pick one canonical URL among duplicates and consolidate signals onto it.. GitHub Pages also pins specific plugin versions (3.10.0 core, olderjekyll-seo-tag/jekyll-sitemapreleases), not just which plugins run. Collections needoutput: trueor they’re never rendered. Drafts, future-dated posts, andpublished: falsedocuments are excluded from a normal build, and--incrementalis experimental — don’t use it for production deploys. Androbots.txtis not auto-generated — make it yourself.
Why Jekyll’s static output is good for SEO
Jekyll is a static site generatorA build tool that pre-renders every page into static HTML files at deploy time, so the server delivers complete HTML without executing code per request — eliminating JavaScript rendering delays for search engine crawlers.: 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. 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 crawl → render → indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., and renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. 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 budgetThe number of URLs an engine will crawl in a timeframe.. 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 generatorA build tool that pre-renders every page into static HTML files at deploy time, so the server delivers complete HTML without executing code per request — eliminating JavaScript rendering delays for search engine crawlers. the lowest-risk architecture for indexability.
The downstream benefits follow:
- Faster TTFBTime to First Byte — the time from the start of a request to when the first byte of the response arrives. It's a diagnostic metric (not a Core Web Vital) and a major input to FCP and LCP; ≤0.8 s is good.. 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 LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good. and the rest of Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data..
- No required JS payload for content → better FCPFirst Contentful Paint — the time from when a page starts loading to when any part of its content (text, image, SVG, or non-white canvas) first renders. Good is ≤1.8 s at the 75th percentile. It's a diagnostic metric, not a Core Web Vital. and LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good. than a hydrating SPA.
- Proper HTTP status codesAn HTTP status code is the three-digit number a server returns with every response to tell a browser or crawler what happened to its request — success, redirect, client error, or server error. For SEO the code matters as much as the content: it tells Google and Bing whether to index a page, follow a redirect, retry later, or drop the URL from the index. at the CDN layer, not client-side error handling.
But none of that changes the fundamentals: static sites still need meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored., sitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., canonical URLs, structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding., and good content. Jekyll’s output is crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index.-friendly; the metadata is on you.
GitHub Pages and Jekyll SEO
The most common Jekyll deployment is GitHub Pages — push to a branch with Pages enabled and GitHub auto-builds the site. That convenience comes with SEO-relevant constraints.
Project sites vs. user/org sites — the URL decision
username.github.io/repo-name(a project site) lives on a shared subdomain with thousands of other unrelated sites. It works fine for docs, demos, and personal projects where the URL itself doesn’t need to carry your brand.- A custom domain (point a
CNAMEat GitHub Pages) puts the site on a URL you control, with HTTPSHTTPS is the encrypted version of HTTP — it uses TLS to authenticate the server and protect data in transit between a browser and a website. Google announced it as a lightweight ranking signal in 2014 and today conditionally prefers HTTPS pages as canonical; Chrome marks plain HTTP pages 'Not Secure.' auto-provisioned by GitHub. For a site where the domain itself matters — a business site, a blog you’re building an audience around — a custom domain from the start avoids a domain migration later.
If you expect to move to a custom domain eventually, set it up early. Any
domain migration means 301 redirectsA 301 redirect is the HTTP status code for a permanent move: it tells browsers and search engines a URL has moved for good, and it's the strongest signal for consolidating a page's ranking signals onto the new URL. Google says permanent redirects don't cause a loss in PageRank. (jekyll-redirect-from handles this) and a
period where inbound links and any accumulated signals point at the old URL —
avoidable churn if you pick the final domain up front rather than switching
later. This is a migration-planning argument, not a claim that a github.io
subdomain is penalized on its own.
baseurl — the #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 linksAn internal link is a hyperlink from one page on a website to another page on the same website. Internal links help search engines discover your pages and pass ranking signals (PageRank and anchor-text context) between them. — 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.)
The plugin whitelist
GitHub Pages runs Jekyll with the --safe flag and only allows a fixed set 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✓ (for 301s when you change URLs)jekyll-paginate✓
Not whitelisted (and silently won’t run, or will error):
jekyll-last-modified-at— needed for accurate sitemap<lastmod>from file timestamps- Custom structured-data plugins
- Anything dropped in a
_plugins/folder
It’s not just which plugins — it’s which versions
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.
The GitHub Actions workaround
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.
The jekyll-seo-tag plugin
This is the official, maintained plugin that covers most of the core metadata
Jekyll doesn’t add on its own. Exactly what it emits depends on which version
you have installed, your _config.yml and front matter, and whether your
layout actually calls {% seo %} — GitHub Pages, for instance, pins a specific
version rather than always shipping the latest release (more on that below).
Check the advanced usage guide
for your version’s exact output, and confirm what actually landed by grepping
your own built HTML rather than assuming it’s complete.
Install:
# Gemfile
gem 'jekyll-seo-tag'# _config.yml
plugins:
- jekyll-seo-tag<!-- _layouts/default.html, before </head> -->
{% seo %}What it generates automatically:
<title>— page title with site name appended (Page Title | Site Name)<meta name="description">— fromdescription:front matter or the site description<link rel="canonical">— built fromsite.url+page.url- Open GraphOpen Graph (OG) tags are `<meta>` elements in a page's head, defined by the Open Graph protocol (ogp.me, created by Facebook), that describe a page as a shareable object — its title, description, image, URL, and type. They control how a link preview card looks when the page is shared on Facebook, LinkedIn, Slack, Discord, WhatsApp, and iMessage. They are not a direct Google ranking factor, though Google reads og:title, og:image, and og:site_name as inputs to how a result appears. tags (
og:title,og:description,og:url,og:site_name,og:image) - Twitter Card tagsTwitter Card tags (now often called \"X Cards\") are <meta name=\"twitter:...\"> tags in a page's <head> that tell X's link-unfurling system what rich preview to build for a shared URL — image, headline, description, and (for player cards) an embeddable frame. The four card types are summary, summary_large_image, app, and player. Every twitter:* property falls back to its og:* equivalent if missing — except twitter:card itself, which has no fallback and must be set explicitly. They are not a Google or Bing ranking factor. (
twitter:card,twitter:title,twitter:description,twitter:creator,twitter:image) - JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. structured data (
BlogPostingfor posts,WebSitefor the home page) - PaginationPagination splits a large set of content — product listings, blog archives, search results — across multiple sequentially numbered URLs. For SEO, each paginated page should be crawlable, indexable, and self-canonical; Google no longer uses rel=prev/next, but Bing still does. meta (next/prev URLs)
Required _config.yml settings — without these 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_imagePer-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 (when your layout already outputs its own):
{% seo title=false %} <!-- suppress the <title> -->
{% seo canonical=false %} <!-- suppress the canonical link -->One gotcha worth flagging: many minimal themes (including minima) ship without
jekyll-seo-tag wired in. Adding the gem to your Gemfile does nothing unless the
theme’s layout actually 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.
It requires url: in _config.yml — without it, sitemap entries have no
domain.
Controlling <lastmod>, in priority order:
last_modified_at:in front matter (best — explicit control)- Post creation date (fallback — often wrong for evergreen content updated later)
- Filesystem modification date (needs the non-whitelisted
jekyll-last-modified-at)
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.
Excluding pages:
# Per-page front matter
sitemap: false
# Global pattern (in _config.yml)
defaults:
- scope:
path: "assets/**/*.pdf"
values:
sitemap: falsePermalink configuration
Permalinks are set globally in _config.yml or overridden per page in front matter.
| 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 slashA trailing slash is the forward slash (/) at the end of a URL — example.com/page/ versus example.com/page. Except at the bare root domain, the two versions are different URLs to search engines, so you pick one format and enforce it., no .html |
none | /:categories/:title.html | No date burial |
| Custom | /:title/ or /:categories/:title/ | Most control — recommended for evergreen content |
Recommended for most sites:
permalink: /:title/
# or
permalink: /:categories/:title/Why avoid date-based URLs for evergreen posts:
- Changing a post’s
date:front matter changes its URL → inbound links break. - Deep hierarchy (
/2019/03/14/post-title/) buries content for no reason. - (For news and journalism, date URLs are fine and expected — this is a per-context call, not a universal rule.)
Collections need their own permalink config:
collections:
case_studies:
output: true
permalink: /case-studies/:name/A warning on changing patterns: once URLs are indexed, switching permalink
styles requires 301 redirectsA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't. (use jekyll-redirect-from). Skip the redirects and
you break link equity and generate 404s in Search ConsoleGoogle's free tool for monitoring crawling, indexing, and search performance..
Collections and SEO
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:
-
output: truemust be set. Without it, collection documents are never rendered as individual HTML files and therefore can’t be indexed. This is a silent indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. killer — the content exists in your repo but never becomes a crawlable page.collections: docs: output: true # REQUIRED for indexable pages permalink: /docs/:name/ -
Each document needs front matter — even an empty
---block. Without it, Jekyll treats the file as a binary static file: no Liquid processing, no metadata, nojekyll-seo-tagintegration.
Note that collections aren’t included in RSS feedsAn RSS or Atom feed is an XML file listing a site's most recently published or updated URLs. Search engines accept it as a sitemap-style discovery signal for fresh content — not a replacement for a full XML sitemap. (only posts are). For large documentation sites, collections are usually the right structure anyway.
Publishing states and incremental builds
Jekyll has several independent switches that control what actually ends up in the built site. Mixing them up produces either pages that silently never get built, or draft/future content that ships to production by accident:
- Drafts (
_drafts/) are excluded from a normal build entirely. They only appear when you runbundle exec jekyll serve --drafts(orbuild --drafts) locally — moving a post into_posts/with a real date is what actually publishes it. - Future-dated posts (a
date:later than the current time) are excluded by default.future: truein_config.yml, or the--futureflag, includes them — handy for local preview, but leaving it on in a production config means scheduled posts go live the moment you build, not on their intended date. published: falsein front matter excludes a document from the build regardless of its date — a separate switch from drafts and future posts, and easy to leave set after testing a page you meant to ship.- Incremental regeneration (
--incremental) is documented by Jekyll as experimental, and it tracks a limited dependency graph — mainly includes and layouts. A page that iteratessite.postsor other collection data (a tag index, an archive, related-posts logic) can go stale under--incrementalwithout Jekyll detecting that the underlying posts changed. Don’t run it as part of a production deploy; use a fullbundle exec jekyll buildfor anything you’re pushing live.
Before trusting any of these, build without the flags you use locally
(--drafts, --future, --incremental) and confirm the output in _site/
matches what you intend to publish — the safest default for a deploy pipeline
is a clean, full, non-incremental build.
Custom head partials with Liquid
When jekyll-seo-tag isn’t enough, you build your own _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 | truncate: 160 }}
{%- else -%}{{ site.description }}
{%- endif -%}">
<link rel="canonical" href="{{ page.url | prepend: site.url }}">
{% seo %}
</head>Key Liquid filters for SEO:
| strip_html— removes tags from an auto-excerpt (essential for clean descriptions)| strip_newlines— removes line breaks from excerpts| truncate: 160— caps a description at 160 characters| prepend: site.url— builds absolute URLs for canonical and OG tags| date_to_xmlschema— ISO 8601 dates for JSON-LDdatePublished/dateModified| default: fallback— fallback when a variable is nil
Custom JSON-LD beyond what 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 is not auto-generated
Jekyll does not create robots.txt. You make it yourself in the site root.
Include an empty front-matter block so Liquid processes the file (so {{ site.url }}
resolves):
---
---
User-agent: *
Allow: /
Sitemap: {{ site.url }}/sitemap.xmlWithout this, there’s no sitemap reference in robots.txt — and some crawlers use
that as a discovery mechanism. (robots.txt controls crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor., not indexing —
for the full picture see crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor..)
Common Jekyll SEO myths
“Jekyll handles SEO automatically.” The static output is crawler-friendly, but metadata requires explicit setup. Default themes often ship with no SEO tags at all.
“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 specific versions it pins, not just which plugins) and,
if your brand depends on the URL, deciding on a custom domain before you build
an audience on github.io.
“Static sites don’t need sitemaps.” Google can find pages via links, but a
sitemap speeds discovery and carries <lastmod> signals. jekyll-sitemap makes it
trivial.
“Jekyll is dead.” It’s in mature maintenance mode — v4.4.1 shipped in January 2025. Minimal new features, but maintained and secure, and GitHub Pages will support it indefinitely. For simple blogs and docs, it’s a solid, boring choice.
“I can use any plugin on GitHub Pages.” No — --safe plus a fixed whitelist.
The fix is GitHub Actions, not more gems.
Where this fits
Jekyll is one of the six generators in the static site generatorsA build tool that pre-renders every page into static HTML files at deploy time, so the server delivers complete HTML without executing code per request — eliminating JavaScript rendering delays for search engine crawlers. cluster — the GitHub Pages default and the one most developers meet first. For the broader context on how Google renders JavaScript and when you actually need it, see the JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. hub. The build-freshness discipline that applies to every SSG — a static site is only as current as its last build — applies to Jekyll too: edits don’t reach search engines until you rebuild and redeploy.
AI summary
A condensed take on the Advanced version:
- Jekyll is a Ruby static site generatorA build tool that pre-renders every page into static HTML files at deploy time, so the server delivers complete HTML without executing code per request — eliminating JavaScript rendering delays for search engine crawlers. and the engine behind GitHub Pages. It emits flat HTML at build time, so content is in the raw response on the first crawl — no JavaScript renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. step, no Wave 2 delay. That’s the core SEO advantage, shared by all static site generators.
- The SEO work is configuration, not architecture. Default themes often have no SEO metadata at all.
jekyll-seo-tag(official plugin) auto-generates title, description, canonical, Open GraphOpen Graph (OG) tags are `<meta>` elements in a page's head, defined by the Open Graph protocol (ogp.me, created by Facebook), that describe a page as a shareable object — its title, description, image, URL, and type. They control how a link preview card looks when the page is shared on Facebook, LinkedIn, Slack, Discord, WhatsApp, and iMessage. They are not a direct Google ranking factor, though Google reads og:title, og:image, and og:site_name as inputs to how a result appears., Twitter Card, and JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. (BlogPosting/WebSite). Add{% seo %}to your layout’s<head>. Overridable per page in front matter.jekyll-sitemapauto-generatessitemap.xml. Both plugins requireurl:in_config.ymlor output is broken (canonical/sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. point at localhost).- Permalinks: prefer
/:title/or/:categories/:title/over date-based URLs for evergreen content (date URLs break if the post date changes). - Collections need
output: true(or they’re never rendered as pages) and front matter on each document (or no metadata processing). - Publishing states: drafts (
_drafts/), future-dated posts, andpublished: falsedocuments are all excluded from a normal build unless you explicitly opt in (--drafts,--future).--incrementalis experimental and can miss pages that depend onsite.posts— don’t use it for a production deploy. robots.txtis NOT auto-generated — create it manually (with empty front matter so Liquid resolves{{ site.url }}in theSitemap:line).- Two GitHub Pages traps: the plugin whitelist (
--safebuild; onlyjekyll-seo-tag,jekyll-sitemap,jekyll-redirect-from,jekyll-paginateamong SEO plugins — others need a GitHub Actions build), and missingbaseurlon project sites, which breaks every canonical URLHow search engines pick one canonical URL among duplicates and consolidate signals onto it.. GitHub Pages also pins specific plugin versions (e.g. Jekyll 3.10.0 core), which can trail the latest upstream release. - If a custom domain matters to your brand, set it up early rather than on
username.github.io/repo— moving domains later means 301s and a period where inbound links point at the old URL. GitHub Pages also pins specific plugin versions (not just which plugins), which can trail the latest upstream release. - Build freshness applies as to any SSG: changes only reach search after a rebuild + redeploy.
Official documentation
Primary-source documentation — from Jekyll, its plugins, GitHub Pages, and the search engines.
Jekyll
- Jekyll docs — the documentation hub.
- Permalinks — the built-in styles and custom patterns.
- Front Matter — the YAML block that drives per-page metadata.
- Collections — custom content types and the
output: truerequirement. - Plugins — how Jekyll’s plugin system works.
- Variables — the
site/pagevariables and Liquid filters used in head partials.
Official plugins
- jekyll-seo-tag — the metadata plugin, plus its advanced usage guide.
- jekyll-sitemap — automatic
sitemap.xml. - jekyll-redirect-from — 301 redirectsA 301 redirect is the HTTP status code for a permanent move: it tells browsers and search engines a URL has moved for good, and it's the strongest signal for consolidating a page's ranking signals onto the new URL. Google says permanent redirects don't cause a loss in PageRank. when you change URLs.
GitHub Pages
- About GitHub Pages and Jekyll — how the auto-build works.
- About custom domains and GitHub Pages — the custom-domain setup.
- pages.github.com/versions.json — the live plugin whitelist and locked dependency versions.
- Jekyll releases and endoflife.date/jekyll — version and maintenance status.
Search engines
- JavaScript SEO basics — the crawl → render → indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. phases (two-wave renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM.) that a static build lets you skip for content.
- In-Depth Guide to How Google Search Works — where renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. sits in the pipeline.
- Core Web Vitals (web.dev) — the performance metrics a fast static site benefits from structurally.
Quotes from the source
There are no on-record quotes from Google or Bing reps specifically about Jekyll. Google doesn’t comment on individual static site generatorsA build tool that pre-renders every page into static HTML files at deploy time, so the server delivers complete HTML without executing code per request — eliminating JavaScript rendering delays for search engine crawlers.. The SEO case for Jekyll rests on Google’s general renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. and static-HTML guidance, which applies to any static site.
Google — renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. is a separate, queued step (the thing Jekyll’s static output removes from the critical path)
- “During the crawl, Google renders the page and runs any JavaScript it finds using a recent version of Chrome.” — Google Search Central docs. With Jekyll, there’s no JavaScript needed to render the body, so this step costs you nothing. Jump to quote
- “Rendering is important because websites often rely on JavaScript to bring content to the page, and without rendering Google might not see that content.” — i.e., if your content is already in the static HTML (as it is with Jekyll), rendering can’t cost you visibility. Jump to quote
Jekyll SEO checklist
A scannable pass to confirm your Jekyll site is set up to rank:
-
url:is set in_config.ymlto your real production domain (notlocalhost:4000). - For a GitHub Pages project site,
baseurl: /repo-nameis set (skip on user/org root sites). -
jekyll-seo-tagis installed and the theme layout actually calls{% seo %}in the<head>. -
jekyll-sitemapis installed and/sitemap.xmlis generating. - SitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. is submitted in Google Search ConsoleA free Google service that reports how a site performs in Google Search and surfaces problems with how Google crawls, indexes, and serves it. It's first-party data straight from Google — but you don't need it to appear in results. and Bing Webmaster ToolsMicrosoft's free portal for monitoring and improving how a site appears in Bing search — the peer to Google Search Console, plus IndexNow instant indexing, richer backlink data, and keyword volumes. Because Bing's index also feeds Microsoft Copilot, it doubles as a window into AI-search visibility..
-
title:,description:, andauthor:are set in_config.yml. - Each post/page has an explicit
description:(don’t rely on auto-excerpt). -
last_modified_at:is on posts and bumped when content changes. - Permalinks use
/:title/or/:categories/:title/for evergreen content (not the date default). - A
robots.txtexists (with empty front matter) and references the sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing.. - Every collection that should be indexedStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. has
output: true. - Every collection document has front matter (even an empty
---). - A custom domain is configured (HTTPSHTTPS is the encrypted version of HTTP — it uses TLS to authenticate the server and protect data in transit between a browser and a website. Google announced it as a lightweight ranking signal in 2014 and today conditionally prefers HTTPS pages as canonical; Chrome marks plain HTTP pages 'Not Secure.' auto-provisioned), not the bare
github.iosubdomain. - URL/permalink changes are paired with
jekyll-redirect-from301s. - Images are compressed before commit (Jekyll doesn’t optimize them at build).
- One trailing-slash convention is enforced via the permalink style (no
/page/vs/pageduplicates). - Production builds run without
--draftsor--future, and no page you intend to ship still haspublished: false. - Production deploys use a full
bundle exec jekyll build, not--incremental(experimental, and can misssite.posts-dependent pages).
Jekyll SEO — cheat sheet
The two essential plugins
| Plugin | Does | Whitelisted on GitHub Pages? | Needs |
|---|---|---|---|
jekyll-seo-tag | Title, description, canonical, OG, Twitter Card, JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. | Yes ✓ | url:, {% seo %} in <head> |
jekyll-sitemap | Auto sitemap.xml | Yes ✓ | url: |
jekyll-redirect-from | 301 redirectsA 301 redirect is the HTTP status code for a permanent move: it tells browsers and search engines a URL has moved for good, and it's the strongest signal for consolidating a page's ranking signals onto the new URL. Google says permanent redirects don't cause a loss in PageRank. | Yes ✓ | redirect_from: in front matter |
jekyll-last-modified-at | Filesystem <lastmod> | No ✗ | GitHub Actions build |
Required _config.yml keys (or output breaks)
| Key | Why |
|---|---|
url: | Canonical URLsHow search engines pick one canonical URL among duplicates and consolidate signals onto it. + sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. entries; without it → localhost |
baseurl: | Project sites under /repo/ — without it → broken canonicals |
title: / description: | Defaults for jekyll-seo-tag |
author: | Author info in JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. |
Permalink styles
permalink: value | Output | Use for |
|---|---|---|
date (default) | /cat/2019/03/14/title.html | Avoid for evergreen |
pretty | /cat/2019/03/14/title/ | Date-relevant content |
none | /cat/title.html | No date burial |
/:title/ | /title/ | Most evergreen sites |
Fast facts
- Jekyll output is static HTML → no Wave 2 renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. delay; content in the first fetch.
robots.txtis NOT auto-generated — create it (with empty front matter).- Collections need
output: trueand front matter, or they don’t indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.. - GitHub Pages builds with
--safe+ a plugin whitelist — non-whitelisted plugins need a GitHub Actions build. - Current upstream Jekyll: v4.4.1 (Jan 2025); GitHub Pages runs a locked
v3.10.0 with its own pinned plugin versions (
jekyll-seo-tag2.8.0,jekyll-sitemap1.4.0,jekyll-feed0.17.0) — check pages.github.com/versions.json for the current list. - If a custom domain matters to your brand, set it up before building an
audience on
username.github.io/repo— avoids a later domain migration.
Build and check your Jekyll site locally
Before trusting production, build locally and verify your metadata is actually in the HTML.
macOS / Linux
# Install dependencies and serve locally (defaults to http://localhost:4000)
bundle install
bundle exec jekyll serve
# Build the production site into _site/ (override the dev url)
JEKYLL_ENV=production bundle exec jekyll build
# Confirm jekyll-seo-tag actually emitted a canonical + title into a built page
grep -i 'rel="canonical"' _site/index.html
grep -i "<title>" _site/index.html
# Confirm the sitemap was generated and points at your real domain (not localhost)
grep -i "<loc>" _site/sitemap.xml | headWindows (PowerShell)
# Install dependencies and serve locally
bundle install
bundle exec jekyll serve
# Build the production site (set the env var for this command)
$env:JEKYLL_ENV="production"; bundle exec jekyll build
# Confirm canonical + title made it into the built HTML
Select-String -Path _site\index.html -Pattern 'rel="canonical"'
Select-String -Path _site\index.html -Pattern "<title>"
# Confirm the sitemap was generated with your real domain
Select-String -Path _site\sitemap.xml -Pattern "<loc>" | Select-Object -First 10If <loc> entries or the canonical show localhost:4000, your url: isn’t set (or
JEKYLL_ENV=production wasn’t applied) — fix _config.yml before deploying.
A starter robots.txt for Jekyll
Save this as robots.txt in your site root. The empty front-matter block is what
makes Liquid process the file so {{ site.url }} resolves:
---
---
User-agent: *
Allow: /
Sitemap: {{ site.url }}/sitemap.xml Patrick's relevant free tools
- SEO Incident Simulator — Practice thirty deterministic technical SEO incident investigations — indexability, crawl controls, redirects, sitemaps, markup, caching, DNS, bot verification, rendering, hreflang, and faceted navigation — with clearly labeled fixture evidence and Find → Fix → Verify handoffs.
- Raw vs. Rendered HTML Checker — See what's in your page's initial HTML versus after JavaScript runs — headless-Chrome rendering only when the page actually needs it, a rendering-strategy verdict (SSR / prerendered / CSR / hybrid), ~15 calibrated JavaScript-SEO checks (noindex, canonicals, robots.txt blocking, links, soft 404s), a side-by-side raw-vs-rendered diff, and shareable reports.
Tools for Jekyll SEO
jekyll-seo-tag— the official metadata plugin (title, description, canonical, OG, Twitter Card, JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal.). The single biggest SEO win for a Jekyll site.jekyll-sitemap— official automaticsitemap.xml.jekyll-redirect-from— generate 301 redirectsA 301 redirect is the HTTP status code for a permanent move: it tells browsers and search engines a URL has moved for good, and it's the strongest signal for consolidating a page's ranking signals onto the new URL. Google says permanent redirects don't cause a loss in PageRank. in front matter when you change permalinks.- GitHub Actions (
actions/jekyll-build-pages,peaceiris/actions-gh-pages) — build Jekyll yourself to escape the GitHub Pages plugin whitelist and run any plugin. - Google Search ConsoleA free Google service that reports how a site performs in Google Search and surfaces problems with how Google crawls, indexes, and serves it. It's first-party data straight from Google — but you don't need it to appear in results. — verify the site, submit the sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., and watch indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed./coverage. Essential for a new Jekyll site.
- Bing Webmaster ToolsMicrosoft's free portal for monitoring and improving how a site appears in Bing search — the peer to Google Search Console, plus IndexNow instant indexing, richer backlink data, and keyword volumes. Because Bing's index also feeds Microsoft Copilot, it doubles as a window into AI-search visibility. — submit the sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. (you can import credentials from GSC).
- View Source / URL InspectionA Google Search Console feature that reports how Google sees one specific URL on a property you own. By default it shows the last-indexed snapshot; a separate \"Test live URL\" mode fetches the current version. — confirm your title, meta descriptionThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through., and canonical are in the raw HTML (they should be, since Jekyll is static).
- PageSpeed InsightsPageSpeed Insights (PSI) is a free Google tool at pagespeed.web.dev that reports two kinds of data for a URL: real-user field data from the Chrome UX Report and a single Lighthouse lab run with the 0–100 Performance score. Only the field Core Web Vitals are what Google uses for ranking. / web.dev — check Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data.; a static Jekyll site should score well, but unoptimized images are the common drag.
Jekyll SEO mistakes to avoid
Concrete mistakes people actually make setting up Jekyll for search — prevention, not diagnosis.
Leaving url: unset in _config.yml
Both jekyll-seo-tag and jekyll-sitemap build absolute URLs from site.url.
Skip it and your canonical tagsA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. and sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. <loc> entries either point at
localhost:4000 (the dev default) or come out with no domain at all — that’s
canonical URLsHow search engines pick one canonical URL among duplicates and consolidate signals onto it. and a sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. that actively mislead crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. instead of helping
them. Do this instead: set url: to your real production domain in
_config.yml before the first deploy, and grep the built _site/ output for
localhost to confirm it didn’t leak through.
Skipping baseurl on a GitHub Pages project site
username.github.io/repo-name sites live under a /repo-name/ subfolder. If
baseurl: /repo-name isn’t set, every canonical URL jekyll-seo-tag generates —
and your internal linksAn internal link is a hyperlink from one page on a website to another page on the same website. Internal links help search engines discover your pages and pass ranking signals (PageRank and anchor-text context) between them. — is missing that subfolder, pointing at a URL that
doesn’t exist. This is the single most common canonical-URL bug on Jekyll project
sites. Do this instead: set baseurl: on project sites (user/org sites at
the domain root don’t need it), and spot-check a built page’s canonical against
its real, live URL.
Assuming a collection is indexable without output: true
Collections (docs sections, case studies, team bios) are Jekyll’s custom content
type, but without output: true in _config.yml, the documents are never
rendered as individual HTML files — the content sits in your repo and simply
never becomes a crawlable page. There’s no error, no warning, just pages that
never exist. Do this instead: set output: true on every collection meant
to be public, and after a build, confirm the expected HTML files actually landed
in _site/.
Using date-based permalinks for evergreen content
The Jekyll default permalink style (/:categories/:year/:month/:day/:title.html)
bakes the publish date into the URL. Change the post’s date: front matter later
— which people do, often just to bump it for a re-publish — and the URL changes
with it, breaking every inbound link and forcing a redirectA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't. cleanup you didn’t
plan for. Do this instead: use /:title/ or /:categories/:title/ for
evergreen posts; reserve date-based permalinks for genuinely date-relevant
content like news, where the date belongs in the URL.
Trying to install a non-whitelisted plugin on GitHub Pages and expecting it to run
GitHub builds Jekyll with the --safe flag, which silently skips or errors on
any plugin outside a fixed whitelist — dropping a gem like
jekyll-last-modified-at or a custom structured-data plugin into _plugins/
and pushing it does not make it run on the default GitHub Pages build. Do this
instead: check the plugin against
pages.github.com/versions.json first;
if it’s not listed, build with GitHub Actions (actions/jekyll-build-pages or a
local build pushed via peaceiris/actions-gh-pages) instead of fighting the
whitelist.
Assuming Jekyll ships a robots.txt
Jekyll does not generate robots.txt — there’s no plugin toggle, no default
file, nothing. A site with no robots.txt at all isn’t broken for indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., but
it also has no Sitemap: reference for crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. that use robots.txt as a
discovery mechanism. Do this instead: create robots.txt yourself in the
site root with an empty front-matter block (--- ---) so Liquid processes it
and {{ site.url }} resolves in the Sitemap: line.
Test yourself: Jekyll SEO
Five quick questions on optimizing Jekyll sites for search. Pick an answer for each, then check.
Resources worth your time
My writing
- JavaScript SEO: A Definitive Guide — renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM., DOM parityWhether the rendered DOM matches what you expect the raw HTML to become., and why static/prerendered output (like Jekyll’s) is the low-risk end of the spectrum.
- The Beginner’s Guide to Technical SEO — where renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. architecture and sitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. fit in the bigger picture.
My speaking
- How Search Works (SlideShare) — my walkthrough of crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor., rendering, indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., and ranking. (My standing disclaimer applies: “This is my understanding of systems… not going to be 100% complete or accurate.”)
From around the industry
- Jekyll docs — the official documentation, including permalinks and collections.
- jekyll-seo-tag — the official metadata plugin and its advanced usage guide.
- jekyll-sitemap — the official sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. plugin.
- About GitHub Pages and Jekyll — GitHub’s own docs on the build process and constraints.
- pages.github.com/versions.json — the authoritative GitHub Pages plugin whitelist and locked versions.
- Google Search Central — JavaScript SEO basics — the rendering phases a static build lets you skip.
- CloudCannon — jekyll-seo-tag showcase — a practitioner walkthrough of the plugin.
Jekyll SEO
Jekyll SEO is the set of technical and on-page practices for optimizing sites built with Jekyll — the Ruby static site generator that powers GitHub Pages. Jekyll outputs flat HTML with no rendering delay, so the SEO work is configuration: jekyll-seo-tag for metadata, jekyll-sitemap for sitemaps, clean permalinks, and a manual robots.txt.
Related: JavaScript SEO
Jekyll SEO
Jekyll is the Ruby-based static site generatorA build tool that pre-renders every page into static HTML files at deploy time, so the server delivers complete HTML without executing code per request — eliminating JavaScript rendering delays for search engine crawlers. originally created by GitHub co-founder Tom Preston-Werner, and it’s the engine behind GitHub Pages. It turns Markdown/HTML plus Liquid templates into a folder of finished, flat HTML files at build time — so a crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. receives complete, rendered content on the first fetch, with no JavaScript renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. step and no “Wave 2” delay. That static output is the foundation of Jekyll’s SEO advantage; it’s the same argument that makes any static site generator the lowest-risk architecture for indexability.
Because the output is already crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index.-friendly, Jekyll SEO is mostly about configuration, not the framework. The core moving parts:
jekyll-seo-tag— the official plugin that auto-generates the title tagThe title tag is the HTML title element in a page's head that specifies the document's title. It's the primary source for the SERP title link and a confirmed light ranking factor — but since August 2021 Google doesn't always show it verbatim., meta descriptionThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through., canonical URLHow search engines pick one canonical URL among duplicates and consolidate signals onto it., Open GraphOpen Graph (OG) tags are `<meta>` elements in a page's head, defined by the Open Graph protocol (ogp.me, created by Facebook), that describe a page as a shareable object — its title, description, image, URL, and type. They control how a link preview card looks when the page is shared on Facebook, LinkedIn, Slack, Discord, WhatsApp, and iMessage. They are not a direct Google ranking factor, though Google reads og:title, og:image, and og:site_name as inputs to how a result appears. and Twitter Card tagsTwitter Card tags (now often called \"X Cards\") are <meta name=\"twitter:...\"> tags in a page's <head> that tell X's link-unfurling system what rich preview to build for a shared URL — image, headline, description, and (for player cards) an embeddable frame. The four card types are summary, summary_large_image, app, and player. Every twitter:* property falls back to its og:* equivalent if missing — except twitter:card itself, which has no fallback and must be set explicitly. They are not a Google or Bing ranking factor., and JSON-LD structured dataJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. from your front matter and_config.yml. Drop{% seo %}in your layout’s<head>.jekyll-sitemap— auto-generates a sitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing..org-compliantsitemap.xml. Both plugins needurl:set in_config.ymlor their output is broken.- Permalinks — avoid the default date-based URLs for evergreen content; prefer
/:title/or/:categories/:title/. robots.txt— not auto-generated; you create it manually as a static file.
The two things that trip people up are the GitHub Pages plugin whitelist (only a fixed set of plugins runs in GitHub’s --safe build; everything else needs a GitHub Actions workaround) and baseurl misconfiguration on project sites, which silently breaks every canonical URL.
Related: JavaScript SEO
Build-time retrieval analysis plus live signals for this exact article. The automatic chunk report includes a deterministic readiness score and is ready without a model download.
Search Console
sampleGA4 traffic (28d)
sampleCloudflare traffic (7d)
sampledCrUX field data (28d, phone)
sampleGoogle NLP entities
localChangelog
Updated Jul 18, 2026.
Editorial summary and recorded change details.Summary
Distinguished upstream Jekyll/plugin versions from GitHub Pages' pinned build, added a publishing-states section (drafts, future posts, published: false, incremental regeneration), and removed unsupported percentage/authority claims (jekyll-seo-tag's coverage, github.io link-equity) in favor of version-specific and migration-focused framing.
Change details
-
Added a Publishing states and incremental builds section covering _drafts/, future-dated posts, published: false, and Jekyll's experimental --incremental regeneration and its site.posts staleness risk.
-
Noted GitHub Pages' pinned plugin versions (Jekyll 3.10.0, jekyll-seo-tag 2.8.0, jekyll-sitemap 1.4.0, jekyll-feed 0.17.0) versus upstream Jekyll 4.4.1, and pointed to pages.github.com/versions.json as the source of truth.
-
Removed the unsupported 'jekyll-seo-tag covers roughly 90%' figure and the 'github.io carries no brand authority / doesn't transfer link equity' claims, replacing them with version-check and migration-planning guidance.
Full comparison unavailable — no prior snapshot was archived for this revision.