# Jekyll

## Getting Started

## Create a connection

1. Sign in to [OpenGraph+](/)
2. Go to your website's **Meta Tags** page
3. Create a new connection and copy your connection URL

Your connection URL looks like `https://$OGPLUS_KEY.ogplus.net`.

## Find your layout

Open `_layouts/default.html` (or whatever layout your pages use). Jekyll uses Liquid templates with YAML frontmatter for page data. This is where you'll add Open Graph meta tags so every page inherits them.

## Add Open Graph meta tags

Add the full set of Open Graph tags to your layout's `<head>`. Jekyll's `page` and `site` variables pull in dynamic values automatically:

```html
<!-- _layouts/default.html -->
<html>
  <head>
    <meta property="og:title" content="{{ page.title | default: site.title }}">
    <meta property="og:description" content="{{ page.description | default: site.description }}">
    <meta property="og:url" content="{{ page.url | absolute_url }}">
    <meta property="og:site_name" content="{{ site.title }}">
    <meta property="og:type" content="{{ page.og_type | default: 'website' }}">
    <meta property="og:image" content="https://$OGPLUS_KEY.ogplus.net{{ page.url }}">
    <meta name="twitter:card" content="summary_large_image">
  </head>
  <body>
    {{ content }}
  </body>
</html>
```

Replace `https://$OGPLUS_KEY.ogplus.net` with the connection URL you copied.

### Tag reference

| Tag | Value | Source |
|-----|-------|--------|
| `og:title` | Page title | `page.title` frontmatter, falls back to `site.title` |
| `og:description` | Page description | `page.description` frontmatter, falls back to `site.description` |
| `og:url` | Canonical URL | `page.url` resolved to absolute URL |
| `og:site_name` | Site name | `site.title` from `_config.yml` |
| `og:type` | Content type | `page.og_type` frontmatter, defaults to `website` |
| `og:image` | Social card image | OpenGraph+ connection URL + page path |
| `twitter:card` | Card format | `summary_large_image` for large previews |

## Dynamic tags from frontmatter

Jekyll pages and posts set their tags via YAML frontmatter. The layout picks up these values automatically:

```markdown
---
title: How to Build a Blog with Jekyll
description: A step-by-step guide to building a fast static blog.
og_type: article
layout: default
---

Your post content here.
```

Product page example:

```markdown
---
title: Premium Widget
description: $49.99 — Our best-selling widget, now available in three colors.
og_type: product
layout: default
---
```

## Shared include

If you use multiple layouts, extract the meta tags into a shared include:

```html
<!-- _includes/open-graph.html -->
<meta property="og:title" content="{{ page.title | default: site.title }}">
<meta property="og:description" content="{{ page.description | default: site.description }}">
<meta property="og:url" content="{{ page.url | absolute_url }}">
<meta property="og:site_name" content="{{ site.title }}">
<meta property="og:type" content="{{ page.og_type | default: 'website' }}">
<meta property="og:image" content="https://$OGPLUS_KEY.ogplus.net{{ page.url }}">
<meta name="twitter:card" content="summary_large_image">
```

Then include it in any layout:

```html
<head>
  {% include open-graph.html %}
</head>
```

## Configure site variables

Set your site-wide defaults in `_config.yml`:

```yaml
title: My Blog
description: A blog about web development
url: https://mysite.com
```

These values are used as fallbacks when pages don't define their own `title` or `description` in frontmatter.

## Verify

Deploy your site and open the preview tool in your OpenGraph+ dashboard. Paste a URL from your site to confirm the meta tags and social card image render correctly.


## Customize

OpenGraph+ captures your page in a headless browser and renders it as an image. You control what gets captured using meta tags in your layout and CSS in your stylesheets.

All of the rendering options below are standard HTML meta tags and CSS, so they work the same regardless of framework. The [HTML, CSS, & HTTP guide](/docs/html-css) covers each one in detail. This page shows how to wire them into a Jekyll site.

## Meta tags

Add these to your layout's `<head>` alongside your `og:image` tag. They're all optional.

```html
<!-- _layouts/default.html -->
<head>
  <meta property="og:image" content="https://$OGPLUS_KEY.ogplus.net{{ page.url }}">
  <meta name="twitter:card" content="summary_large_image">

  <!-- Render at 800px wide instead of the default -->
  <meta property="og:plus:viewport:width" content="800">

  <!-- Only capture this element instead of the full page -->
  <meta property="og:plus:selector" content=".post-header">

  <!-- Inject inline styles on the captured element -->
  <meta property="og:plus:style" content="padding: 60px; background: #0f172a; color: white;">
</head>
```

See the [Rendering](/docs/html-css/rendering) guide for what each meta tag does and how they interact.

## CSS styling

OpenGraph+ adds a `data-ogplus` attribute to your `<html>` element during capture. Use it in your stylesheets to hide navigation, adjust spacing, or restyle anything for the social card without affecting your actual site.

```css
/* assets/css/style.css */
html[data-ogplus] {
  nav { display: none; }
  footer { display: none; }
  .hero { padding: 60px; }
}
```

If you're using Tailwind, there's a plugin that gives you `ogplus:` variants like `ogplus:hidden` and `ogplus-twitter:bg-sky-500`. See [CSS Styling](/docs/html-css/data-attributes) for plain CSS examples and [Tailwind setup](/docs/html-css/data-attributes#tailwind-css).

## Templates

For fully custom social card layouts that pull content from your page, use `<template>` elements. These let you build a completely different layout for screenshots without touching your visible page.

```html
<!-- _layouts/default.html -->
<template id="ogplus">
  <div style="padding: 48px; background: #0f172a; color: white; height: 100%;">
    <h1 style="font-size: 48px;">
      ${document.querySelector('h1')?.textContent}
    </h1>
  </div>
</template>
```

See the [Templates](/docs/html-css/templates) guide for expression syntax, platform-specific templates, and full examples.

## Testing locally

The [Preview Bookmarklet](/docs/html-css/bookmarklet) sets the `data-ogplus` attribute in your browser so you can see how your CSS and Tailwind variants look without deploying or waiting for a real crawler to hit your page.

## Full example

A layout with all the pieces together:

```html
<head>
  <meta property="og:title" content="{{ page.title | default: site.title }}">
  <meta property="og:description" content="{{ page.description | default: site.description }}">
  <meta property="og:image" content="https://$OGPLUS_KEY.ogplus.net{{ page.url }}">
  <meta name="twitter:card" content="summary_large_image">
  <meta property="og:plus:selector" content=".post-header">
  <meta property="og:plus:style" content="padding: 60px; background-color: #0f172a; color: white;">
  <meta property="og:plus:viewport:width" content="800">
</head>
```


## Caching

OpenGraph+ reads HTTP cache headers from your pages to decide when to re-render social card images. Jekyll generates static files, so cache headers are set by your hosting provider.

This page covers the Jekyll side. For how OpenGraph+ handles caching at the HTTP level, see the [HTTP Caching](/docs/html-css/caching) guide.

## GitHub Pages

GitHub Pages sets its own cache headers and doesn't allow customization. Use meta tag overrides instead:

```html
<meta property="og:plus:cache:max_age" content="86400">
```

Add this to your layout's `<head>` to set a 24-hour cache TTL regardless of what GitHub Pages returns.

## Netlify

Create a `_headers` file in your project root (Jekyll will copy it to the output):

```
# _headers
/*
  Cache-Control: public, max-age=604800
```

Or use `netlify.toml`:

```toml
[[headers]]
  for = "/*"
  [headers.values]
    Cache-Control = "public, max-age=604800"
```

## Vercel

Add headers to your `vercel.json`:

```json
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "public, max-age=604800" }
      ]
    }
  ]
}
```

## Cloudflare Pages

Create a `_headers` file in your project root:

```
# _headers
/*
  Cache-Control: public, max-age=604800
```

## Meta tag overrides

Always available regardless of hosting provider. Add these to your layout's `<head>`:

```html
<meta property="og:plus:cache:max_age" content="86400">
<meta property="og:plus:cache:etag" content="v1.2.3">
```

When the etag changes, OpenGraph+ re-renders even if the cache hasn't expired.

For per-page control, use Jekyll frontmatter with a custom include:

```html
<!-- _includes/ogplus-cache.html -->
{% if page.ogplus_cache_etag %}
<meta property="og:plus:cache:etag" content="{{ page.ogplus_cache_etag }}">
{% endif %}
```

```yaml
# In your post's frontmatter
---
title: My Post
ogplus_cache_etag: "rev-5"
---
```

See the [HTTP Caching guide](/docs/html-css/caching) for the full reference on cache headers and meta tag overrides.

## Purging

Force an immediate re-render from the OpenGraph+ dashboard by purging the cached image for any URL.

## Recommendations

| Page type | Strategy |
|-----------|----------|
| Static pages | Long TTL via hosting headers (7 days) |
| Blog posts | Medium TTL (1 day) + etag meta tag |
| GitHub Pages | Meta tag overrides (no header control) |
| Frequently updated | Short TTL + etag for instant invalidation |


## Troubleshooting

## Meta tags not appearing

Check that the meta tags are in your layout's `<head>`. View the page source (not browser DevTools) to confirm the tags are present in the raw HTML.

If you use multiple layouts, add the meta tags to each one or use a shared include:

```html
<head>
  {% include ogplus.html %}
</head>
```

## Wrong image showing

Social platforms and OpenGraph+ cache images. If you've updated your page but see the old image:

1. Purge the cached image from the OpenGraph+ dashboard
2. Wait a few minutes for the new image to render
3. Clear the platform's cache using their debugger tools (see below)

## Liquid syntax

Jekyll uses Liquid (`{{ }}`). The connection URL is a plain string, so there are no syntax conflicts with `https://$OGPLUS_KEY.ogplus.net`.

If you're generating `og:plus:cache:etag` dynamically with Liquid variables, make sure the variable resolves correctly:

```html
<meta property="og:plus:cache:etag" content="{{ page.last_modified_at | date: '%s' }}">
```

## Social platforms not updating

Social networks cache images aggressively. After purging from OpenGraph+, use each platform's debugger to force a refresh:

- **Facebook**: [Sharing Debugger](https://developers.facebook.com/tools/debug/)
- **Twitter/X**: [Card Validator](https://cards-dev.twitter.com/validator)
- **LinkedIn**: [Post Inspector](https://www.linkedin.com/post-inspector/)

## Purging

To force OpenGraph+ to re-render an image:

1. Go to your OpenGraph+ dashboard
2. Navigate to the website's cache page
3. Enter the URL and purge

## Testing

Use the preview tool in your OpenGraph+ dashboard to test how any page on your site renders as a social card. Paste a URL and see the result immediately.

