なぜ Actions で生成する?
手動でOGP画像を作る手間をなくし、記事追加のたびに自動で最新の画像を生成できます。
自動化
記事を push するだけでOGP画像が自動生成・コミットされます。手作業はゼロ。
デザイン統一
全記事で同じテンプレート・フォント・配色を使えます。ブランド感を保てます。
日本語対応
Noto Sans JP などのフォントを Actions 上で自動ダウンロード・埋め込み。
Pages と統合
サイトビルド前にOGP生成を挟むだけ。1つのワークフローで完結します。
ワークフロー集
用途に合わせて選べる3パターン。YAMLをコピーしてそのまま使えます。
01 Satori + Resvg 推奨
SVGテンプレートから高速にPNGを生成。Puppeteerより軽量で、CSSライクなレイアウトが可能です。Node.jsプロジェクトに最適です。
.github/workflows/generate-ogp.ymlname: Generate OGP Images
on:
push:
branches: ["main"]
paths:
- "content/**"
- "scripts/ogp/**"
workflow_dispatch:
jobs:
ogp:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: Download Japanese font
run: |
mkdir -p scripts/ogp/fonts
curl -sL -o scripts/ogp/fonts/NotoSansJP-Bold.otf \
"https://github.com/googlefonts/noto-cjk/raw/main/Sans/OTF/Japanese/NotoSansJP-Bold.otf"
- name: Generate OGP images
run: node scripts/ogp/generate.js
- name: Commit generated images
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add public/ogp/
if git diff --cached --quiet; then
echo "No changes"
exit 0
fi
git commit -m "chore: update ogp images [skip ci]"
git push
scripts/ogp/generate.js の例
const fs = require('fs');
const path = require('path');
const satori = require('satori').default;
const { Resvg } = require('@resvg/resvg-js');
const fontData = fs.readFileSync(path.join(__dirname, 'fonts/NotoSansJP-Bold.otf'));
const posts = [
{ slug: 'hello-world', title: 'GitHub Actions で OGP 画像を自動生成する', date: '2026-08-25' },
];
async function generate() {
const outDir = path.join(process.cwd(), 'public/ogp');
fs.mkdirSync(outDir, { recursive: true });
for (const post of posts) {
const svg = await satori(
{
type: 'div',
props: {
style: {
width: 1200, height: 630,
background: 'linear-gradient(135deg, #0d1117, #161b22)',
color: '#c9d1d9', fontFamily: 'Noto Sans JP',
padding: 60, display: 'flex',
flexDirection: 'column', justifyContent: 'center',
},
children: [
{
type: 'div',
props: {
style: { fontSize: 56, fontWeight: 700, lineHeight: 1.4, color: '#58a6ff' },
children: post.title,
},
},
{
type: 'div',
props: {
style: { marginTop: 40, fontSize: 28, color: '#8b949e' },
children: post.date,
},
},
],
},
},
{
width: 1200, height: 630,
fonts: [{ name: 'Noto Sans JP', data: fontData, weight: 700 }],
}
);
const resvg = new Resvg(svg, { fitTo: { mode: 'original', original: 1 } });
const pngData = resvg.render();
fs.writeFileSync(path.join(outDir, `${post.slug}.png`), pngData.asPng());
}
}
generate().catch(console.error);
package.json に追加する依存
{
"devDependencies": {
"satori": "^0.12.0",
"@resvg/resvg-js": "^2.6.2"
}
}
ポイント:
paths フィルタで content ディレクトリ変更時のみ実行し、無駄なビルドを防ぎます。[skip ci] を入れて無限ループを回避しています。
02 Pillow シンプル
Node.js を使わず、Pythonだけで完結したい場合。依存が少なく、軽量に動作します。
.github/workflows/generate-ogp-pillow.ymlname: Generate OGP with Pillow
on:
push:
branches: ["main"]
paths:
- "content/**"
- "scripts/generate_ogp.py"
workflow_dispatch:
jobs:
ogp:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install pillow
- name: Download font
run: |
mkdir -p fonts
curl -sL -o fonts/NotoSansJP-Bold.otf \
"https://github.com/googlefonts/noto-cjk/raw/main/Sans/OTF/Japanese/NotoSansJP-Bold.otf"
- run: python scripts/generate_ogp.py
- name: Commit
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add public/ogp/
if git diff --cached --quiet; then exit 0; fi
git commit -m "chore: update ogp images [skip ci]"
git push
scripts/generate_ogp.py の例
from PIL import Image, ImageDraw, ImageFont
import os
os.makedirs("public/ogp", exist_ok=True)
posts = [
{"slug": "hello-world", "title": "GitHub Actions で OGP 画像を自動生成する"},
]
W, H = 1200, 630
title_font = ImageFont.truetype("fonts/NotoSansJP-Bold.otf", 56)
meta_font = ImageFont.truetype("fonts/NotoSansJP-Bold.otf", 28)
for post in posts:
img = Image.new("RGB", (W, H), "#0d1117")
draw = ImageDraw.Draw(img)
draw.text((60, 200), post["title"], fill="#58a6ff", font=title_font)
draw.text((60, 500), "2026-08-25", fill="#8b949e", font=meta_font)
img.save(f"public/ogp/{post['slug']}.png")
03 Pages デプロイ前に統合 統合
OGP生成とサイトビルド・Pagesデプイを1つのワークフローにまとめます。成果物管理がシンプルになります。
.github/workflows/pages.ymlname: Build, Generate OGP, and Deploy
on:
push:
branches: ["main"]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# --- OGP生成 ---
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: Download font
run: |
mkdir -p scripts/ogp/fonts
curl -sL -o scripts/ogp/fonts/NotoSansJP-Bold.otf \
"https://github.com/googlefonts/noto-cjk/raw/main/Sans/OTF/Japanese/NotoSansJP-Bold.otf"
- run: node scripts/ogp/generate.js
# --- サイトビルド ---
- run: npm run build # Vite / Astro / Next.js など
# OGP画像を成果物にコピー
- run: mkdir -p dist/ogp && cp -r public/ogp/* dist/ogp/ || true
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: ./dist
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/deploy-pages@v4
id: deployment
ポイントまとめ
比較表
| 方式 | 速度 | 表現力 | 向き |
|---|---|---|---|
| Satori + Resvg | 速い | 高い(CSSライク) | Node.js プロジェクト |
| Pillow | 速い | 低〜中 | Python / 軽量志向 |
| Puppeteer | 遅い | 最高(HTML/CSS) | 複雑なレイアウト |
よくある注意点
- OGP推奨サイズは 1200 x 630 px
- 日本語フォントは Actions runner に入っていないので必ずダウンロードまたはリポジトリに含める
- コミットメッセージに
[skip ci]を入れて無限ループを防ぐ pathsフィルタで content 変更時のみ実行すると無駄なビルドを減らせる
さっそく試してみる
上のYAMLをコピーして .github/workflows/ に貼り付けるだけで始められます。