Guide
Jekyll and GitHub Pages Open Graph image
On Jekyll and GitHub Pages, the share tags almost always come from the jekyll-seo-tag plugin. It prints og:image only when the page has an image in its front matter, and it makes that URL absolute only when url is set. Most bare Jekyll links are missing one of those two things.
Is the plugin running?
The plugin’s installation steps are three: add gem 'jekyll-seo-tag' to the Gemfile, list it under plugins in _config.yml, and put {% seo %} right before </head> in your layout. A layout without that tag prints none of the Open Graph or X card tags, even with the gem installed.
# _config.yml
plugins:
- jekyll-seo-tag
GitHub Pages does not turn it on for you. GitHub’s About GitHub Pages and Jekyll lists the plugins that are enabled by default and cannot be disabled, and jekyll-seo-tag is not on that list. You enable others by adding them to plugins. The default minima theme already calls the tag in its _includes/head.html ({%- seo -%}). A theme or layout you wrote yourself may not.
Where og:image comes from
In the plugin’s template.html, og:image and twitter:image are printed only when the page has an image. The image is read in image_drop.rb from the page’s image front matter: the string itself, or image.path, then image.facebook, then image.twitter when it is an object. Nothing else. The plugin does not use site.logo for og:image (the usage docs describe logo for the publisher in structured data), and it does not pick the first picture in the post.
---
title: My post
description: One sentence people will see under the title.
image:
path: /assets/images/my-post-card.png
width: 1200
height: 630
alt: A short description of the picture
---
The object form adds og:image:width, og:image:height, and og:image:alt (also twitter:image:alt), per the advanced usage docs. A path that starts with / is resolved from the site root. A path without a leading slash is resolved from the page’s own folder, so image: card.png on /blog/my-post/ points to /blog/my-post/card.png.
When there is an image, twitter:card is summary_large_image unless the page or _config.yml sets twitter.card. Without an image it is summary. The title is the page title (plus title_category when set), falling back to the site title. The description is the page description, then the post excerpt, then the site description, cut to 100 words unless seo_description_max_words says otherwise. og:type is article when the page has a date, which posts do, and website otherwise.
One default image for every page
Because there is no built-in fallback, a page without image has no og:image at all. The advanced usage docs give the fix: a front matter default in _config.yml.
# _config.yml
defaults:
- scope:
path: ""
values:
image: /assets/images/default-card.png
A page’s own image still wins over the default. Make the default a 1200×630 PNG or JPEG: OG image size and OG image format say why, and the Open Graph image resizer crops one in the browser. Restart jekyll serve after editing _config.yml; it does not reload that file.
Set url, and don’t ship the serve output
Crawlers need an absolute https:// image URL (absolute OG image URL covers why). The plugin builds it with Jekyll’s absolute_url filter. In Jekyll’s url_filters.rb, that filter returns a relative path when url is empty. The template also skips og:url and the canonical link when url is not set.
# _config.yml
url: "https://www.example.com" # no trailing slash
baseurl: "" # "/repo-name" for a project site
The usage docs say site.github.url is used by default, and GitHub Pages always runs jekyll-github-metadata, so a site built by GitHub Pages usually gets a URL even without the line. A site built anywhere else (Netlify, Cloudflare Pages, your own CI) does not. If you use a custom domain on GitHub Pages, set url to it anyway so the tags match the address people share.
The other trap is jekyll serve. In serve.rb, Jekyll replaces url with a localhost address when the environment is development, which is the default. If you upload the _site folder that serve left behind, every og:image points at http://localhost:4000. Deploy the output of JEKYLL_ENV=production bundle exec jekyll build instead.
Check the live page, then re-scrape
Read what the server sends, not your local preview:
curl -sL https://www.example.com/blog/my-post/ | grep -iE 'og:image|og:url|twitter:card'
You want one og:image with an absolute https:// URL on your real domain, and a twitter:card of summary_large_image. If there is no og:image, the page has no image and no default. If there are no Open Graph tags at all, the layout does not call {% seo %} or the plugin is not in plugins. If the URL starts with / or localhost, fix url and rebuild. Two og:image tags usually means a hand-written tag in the layout on top of the plugin’s; keep one.
Then open the image URL itself: it should return 200 with an image content type to an anonymous request. If the tag is right and a platform still shows the old card, it cached the earlier fetch. Clear Open Graph cache lists the debugger or workaround for each app, and og:image not showing walks the other causes. When most of the site is missing an image, the site audit will find every page missing og:image.
Using Hugo or Eleventy instead? See Hugo Open Graph image or Eleventy Open Graph image. Next.js and Astro are in Open Graph tags by framework. Hosted builders are in WordPress, Ghost, and the other site-builder guides on the guides page.
FAQ
Why does my Jekyll page have no og:image?
jekyll-seo-tag prints og:image only from the page’s image front matter. It does not fall back to site.logo or to a picture in the post. Set image on the page, or add a front matter default in _config.yml.
Why is the og:image a relative path or a localhost URL?
A relative path means url is not set in _config.yml. A localhost URL means the files came from jekyll serve, which swaps in a local address in development. Set url and deploy a production jekyll build.
Does GitHub Pages add Open Graph tags automatically?
No. jekyll-seo-tag is not one of the plugins GitHub Pages enables by default. Add it to plugins and make sure your layout calls {% seo %}. The minima theme already does.
What does a CardScope check cost?
Previews for X, LinkedIn, Slack, Discord, iMessage, Bluesky, Threads, Mastodon, WhatsApp, Telegram, and Microsoft Teams, plus the fix checklist, are Pro: $19 once via Stripe Checkout. No account. A sample report shows the shape of the result before you pay. A free partial check names whether og:image and og:title are set. We parse HTML in memory and discard it — we do not store the page.