記事の URL を X(旧 Twitter)などに貼ると、タイトル入りの画像がカードで表示されることがあります。あの画像を記事ごとに画像編集ソフトで作るのは、正直かなり面倒です。タイトルを1文字直しただけで、画像も作り直しになります。

そこでこのサイトでは、記事のタイトルから画像を自動で作るようにしました。この記事の先頭の画像も、手で作ったものではありません。

この記事は、Astro で作ったサイトを Cloudflare Workers で公開していることを前提にしています。まだの場合は、先に次の記事を読んでおくと話がつながります。

前提の記事サンプルサイトを Astro に移行して Cloudflare で公開するまでHTML だけで作ったサンプルサイトを静的サイトジェネレーター Astro へ移行し、Cloudflare Workers で再公開するまでの最短手順と、Next.js・Hugo・11ty との比較を解説します。

使っているライブラリのバージョンと Cloudflare の料金は、2026年9月15日時点のものです。

アイキャッチと OGP 画像は同じものを指す

記事の先頭や一覧に置く画像を、ブログでは「アイキャッチ」と呼びます。一方、SNS に URL を貼ったときにカードとして出る画像は、「OGP 画像」と呼ばれます。OGP は、SNS にページのタイトルや画像を伝えるための書き方の決まりのことです。

呼び方は違いますが、このサイトでは同じ画像を両方に使っています。ページの HTML に次の1行があると、SNS はこの画像をカードに表示します。

記事ページの HTML(抜粋)
<meta property="og:image" content="https://growai-lab.com/og/cloudflare-vs-rental-server.png">
<meta name="twitter:card" content="summary_large_image">

2行目の summary_large_image は、X で画像を大きく表示するための指定です。画像の大きさは、OGP 画像でよく使われる横1200×縦630ピクセルにしています。

できあがる画像:タイトルとカテゴリーが自動で入る

実際に自動で作られた画像がこちらです。サイトの見た目に合わせて、ターミナル(黒い画面)風のデザインにしました。

記事「Cloudflare とレンタルサーバーの違いと、Cloudflare での公開が選ばれている理由」のアイキャッチ画像。黒い画面風の枠の中に、記事の種類とカテゴリー、2行のタイトル、記事の URL、サイト名が入っている

画像に入る文字は、すべて記事ファイルの先頭にある設定欄(frontmatter と呼びます)から取っています。

画像の場所元になる情報
上の小さな文字記事の種類(Guide など)とカテゴリー名
大きなタイトルheading(改行位置を指定したタイトル)。なければ title
左下記事の URL
右下サイト名

なぜビルドするだけで画像ができるのか

答えを先に書くと、Satori と sharp という2つのライブラリで画像を描いていて、そのライブラリをビルドのときに動かしているからです。

「自動で画像を作る」と聞くと、画像生成 AI に描いてもらっているのかと思うかもしれません。実際には、もっと単純な仕組みで動いています。まずは画像生成 AI との違いから説明します。

画像生成 AI ではなく、決まった型に文字を流し込んでいる

画像生成 AI は、「黒い画面にタイトルが書かれた画像」のような言葉を受け取り、その内容に合いそうな絵を新しく描き起こします。同じ言葉で頼んでも、仕上がりは毎回少しずつ変わります。

このサイトの仕組みは、年賀状ソフトの宛名印刷に近いものです。はがきのデザインは1つに決めておき、宛名の欄にだけ一人ひとりの住所と名前を差し込んで刷りますよね。OGP 画像も同じで、背景の色や文字の位置といった型は先に決めてあり、そこに記事ごとのタイトルとカテゴリーを差し込んで描いています。

比べる点画像生成 AIこの記事の仕組み
何をもとに作るか「こんな画像」という言葉による指示決めておいた型と、記事のタイトルなどの文字
どうやって描くか指示に合いそうな絵を、新しく描き起こす型のとおりの位置に、文字や四角形を並べる
同じ記事で作り直すと毎回少しずつ違う画像になりやすい何度作っても、まったく同じ画像になる
文字の正確さ文字が崩れたり、違う字になったりすることがあるフォントの字をそのまま使うので、崩れない

型をどう組むかは、Claude Code にコードとして書いてもらいました。AI の手を借りたのは型を作るときだけで、画像を作るたびに AI が動くわけではありません。

描くのは Satori、仕上げるのは sharp

ライブラリとは、ほかのプログラムから呼び出して使う、機能のまとまりのことです。型に文字を差し込んで画像にする作業は、次の2つのライブラリのリレーで進みます。

  • Satori:HTML と CSS の書き方で組んだ型を、SVG 画像にする(Vercel が公開)
  • sharp:画像の形式を変換したり、大きさを変えたりする(Lovell Fuller さんが開発)
画像ができるまで
記事のタイトルやカテゴリー
  ↓
型(背景は黒、上にタイトル、右下にサイト名 …)に差し込む
  ↓ Satori(下書き係)
SVG:形や文字を座標で書いた画像
  ↓ sharp(仕上げ係)
PNG:SNS でそのまま使える、普通の画像

Satori は、Web ページを作るときと同じような書き方で型を受け取り、SVG という形式の画像にします。SVG は「この位置に、この形を、この色で描く」という指示を並べた画像です。文字も、フォントファイルから字の形を読み取って、図形として描き込みます。

ただ、SVG のままでは SNS のカード画像に使えないことがあります。そこで sharp が、SVG を写真と同じような点の集まりの画像(PNG)に変換します。

どちらも無料で使えるオープンソースのライブラリで、外部のサービスには頼みません。画像づくりは、すべてビルドを動かしているコンピューターの中で完結します。

その2つのライブラリを、ビルドのときに動かしている

では、Satori と sharp はいつ動いているのでしょうか。答えは、ビルドのときです。

Astro のサイトには、公開の前にひと手間あります。npm run build を実行すると、Astro が src/pages にあるファイルを1つずつ動かし、できあがった HTML などを dist フォルダに書き出します。この作業をビルドと呼びます。公開されるのは dist の中身です。

ここで大事なのは、Astro が「動かして、出てきたものをファイルに保存する」だけで、出てきたものが文章でも画像でも区別しないことです。そして、src/pages に置いたファイルの名前が、そのまま出来上がるファイルの名前になります。

src/pages に置くファイルビルドで出来上がるファイル中身
privacy.astroprivacy.htmlHTML(文章のページ)
rss.xml.tsrss.xmlRSS(更新情報)
og/[slug].png.tsog/記事名.pngPNG(画像)

表の3行目が、画像を作るファイルです。中で Satori と sharp を呼び出して、できた画像を返すように書いてあります。ビルドのときに Astro がこのファイルを動かすので、そのたびに2つのライブラリも動いて、画像ファイルが保存されるわけです。

ファイル名の [slug] は、「ここに記事の名前が入る」という穴あきの名前です。1つのファイルから、記事の数だけ画像を作るための仕掛けになっています。中身を流れだけにすると、こうなっています。

src/pages/og/[slug].png.ts(流れだけ抜き出したもの)
// ① どの記事の分を作るか、記事の一覧を返す
export async function getStaticPaths() {
const articles = await getArticles();
return articles.map((article) => ({ params: { slug: article.id }, props: { article } }));
}

// ② 記事1本ぶんの画像を作って返す(renderOgImage の中で Satori と sharp が動く)
export async function GET({ props }) {
const png = await renderOgImage({ lines: props.article.data.heading });
return new Response(png, { headers: { 'Content-Type': 'image/png' } });
}

ビルドのとき、Astro は次の順に動きます。

  1. ① を動かして、「publish-with-cloudflare、migrate-to-astro、…」という記事の一覧を受け取る
  2. 一覧の1件ごとに、[slug] の穴に記事の名前を入れて ② を動かす(ここで Satori と sharp が画像を描く)
  3. ② が返した画像を、dist/og/publish-with-cloudflare.png のような名前で保存する

記事が5本なら5回、20本なら20回動きます。記事を増やすと画像も自動で1枚増えるのは、この仕組みのおかげです。

ビルドのときに作るから、表示が速くて料金もかからない

画像は、閲覧者がページを開くたびに作っているわけではありません。ビルドのときに一度作ったら、あとは完成品のファイルを配るだけです。

  • ページを開くたびに画像を作る必要がないので、表示が速い
  • Cloudflare は画像ファイルを配っているだけなので、料金がかからない(詳しくは料金の節
  • 記事のタイトルを直すと、次のビルドで画像も作り直される

反対に、ビルドしないと画像は変わりません。タイトルを直したら、いつもどおり GitHub に push して、Cloudflare にビルドしてもらえば反映されます。

Claude Code に作ってもらう

仕組みがわかったら、実際のコードは Claude Code に任せてしまいましょう。Astro のサイトを開いた状態で、次の指示を渡します。条件を先に書いておくと、あとで直してもらう回数が減ります。

Claude への指示の例:OGP 画像の自動生成
Astro のサイトで、記事ごとのアイキャッチ(OGP 画像)をビルドのときに自動で作ってください。
- 画像は横1200×縦630の PNG にして、/og/記事ファイル名.png に出力する
- 画像には、記事の frontmatter の title(改行位置を指定する heading があればそちら)と、カテゴリー名、サイト名を入れる
- デザインはサイトの配色に合わせる(色は style.css の変数を参考にする)
- 画像の生成には satori と sharp を使い、外部の API や有料のサービスは使わない
- 日本語のフォントは @fontsource の woff ファイルを使う
- タイトルが長くても1行の途中で折り返さないよう、文字の大きさを自動で調整する
- 記事ページの og:image と twitter:card(summary_large_image)に、この画像を設定する
- ビルドして、できあがった画像を実際に開いて、日本語が表示されているか確認してから見せてください

最後の1行は必ず入れておくのがおすすめです。画像は、コードを読むだけでは正しく描けているかわかりません。実際に開いて確認してもらうことで、次の節で紹介するつまずきを Claude Code 自身が見つけてくれます。

作業が終わると、次のようなファイルが追加されます。

追加されるファイルの例
src/pages/og/[slug].png.ts   記事ごとに画像を返す(記事の数だけ動く)
src/utils/og-image.ts        レイアウトを組み立て、Satori と sharp で PNG にする
src/layouts/BaseLayout.astro og:image と twitter:card を出力するように変更

デザインを変えたくなったら、画像を見ながら言葉で頼めば直してもらえます。

Claude への指示の例:デザインの変更
OGP 画像のデザインを変更してください。
- サイト名を右下から左上に移す
- タイトルの文字をもう少し大きくする
- 変更前と変更後の画像を並べて見せてください

作ってみてつまずいたところ

このサイトで実装したときに、実際に引っかかった点です。どれも、画像を開いて見ないと気づけないものでした。

実体験ヒント:画像から日本語の文字が消える

最初は、画像の文字がまったく描かれませんでした。Web ページ向けの日本語フォントは、文字の種類ごとに細かく分けて配布されていることがあり、太字だけでも122個のファイルがありました。これを全部同じ名前で Satori に渡したところ、使われたのは1つ目のファイルだけで、そこに入っていない文字は描かれなかったのです。

次に、japanese(日本語)と latin(英字)のまとまったファイル2つに絞りました。それでも同じ名前で登録したままだと、今度は英字の「Cloudflare」だけが描かれ、日本語が空白になりました。2つを別々の名前で登録し、両方を使うように指定したところで、ようやく日本語も英字も表示されました。

Claude への指示の例:日本語が表示されないとき
OGP 画像で、英字は表示されるのに日本語が表示されません。フォントの読み込み方を見直してください。
- satori に渡しているフォントファイルと、登録している名前を一覧にする
- 日本語用と英字用のフォントを別の名前で登録し、両方を使うように指定する
- 直したら画像を作り直して、日本語が表示されているか開いて確認する

注意点:フォントは woff2 ではなく woff を使う

Web ページで使うフォントは、軽い woff2 形式が主流です。ところが Satori は、TTF・OTF・WOFF の3つの形式にしか対応していません(Satori 0.33.4 の README による)。@fontsource のパッケージには woff と woff2 の両方が入っているので、woff のほうを指定します。

実体験ヒント:長いタイトルで「と、」だけが次の行に落ちる

「Cloudflare とレンタルサーバーの違いと、」のような長い行では、最後の「と、」だけが次の行にはみ出しました。見た目がかなり不格好です。

そこで、1行の文字数から文字の大きさを決めるようにしました。英字は日本語のおよそ半分の幅として数え、いちばん長い行が枠に収まる大きさ(最大60ピクセル)にしています。改行したい位置は、記事の heading で指定しておきます。

料金がかからない理由を数字で確かめる

画像を作るのも配るのも、料金はかかりません。Cloudflare の公式ドキュメント(2026年9月15日時点)の数字と、このサイトでの実測を並べるとこうなります。

かかりうる場所このサイトでの実測Cloudflare の無料プラン
画像を作る時間(ビルド)1枚あたり約0.2〜0.4秒(手元のパソコンで計測)ビルドは月3,000分まで無料
画像の配信普通の画像ファイルとして配信HTML や画像などのファイルを配る回数(リクエスト数)は、無料・無制限
ファイルの数と大きさ1枚あたり約60KB、記事1本につき1枚1回の公開につき20,000ファイル、1ファイル25MiB(約26MB)まで
ライブラリとフォントSatori・sharp、Zen Kaku Gothic New・JetBrains Monoどれもオープンソースで無料。フォントは SIL Open Font License

記事が100本になっても、画像を作る時間は1分もかからない計算です。月3,000分の枠にも、ビルド1回あたり20分の上限にも、まだかなり余裕があります。

注意点:ページを開くたびに画像を作る方式にすると、無料枠を使う

画像をビルドのときに作らず、閲覧者がアクセスするたびに Worker(Cloudflare の上で動くプログラム)で作る方式もあります。その場合は画像を作るたびにプログラムが動くので、無料プランの「1日10万回まで」という上限に数えられます。記事のタイトルから作るだけなら、この記事のようにビルドのときに作る方式で十分です。

公開したら、画像の URL を開いて確かめる

GitHub に push して Cloudflare のビルドが終わったら、2か所を確認します。

  1. https://<サイトの URL>/og/<記事ファイル名>.png を開き、画像が表示されるか
  2. 記事ページの HTML の og:image が、その画像の URL を指しているか

2つ目はブラウザで見るより、Claude Code に頼むほうが早く済みます。

Claude への指示の例:公開後の確認
本番の記事ページ https://<サイトの URL>/articles/<記事ファイル名> の OGP 画像を確認してください。
- HTML の og:image と twitter:card の値を教えてください
- og:image の URL を開いたとき、PNG の画像が返ってくるか(ステータスコードと Content-Type)を確認してください

なお、SNS はカードの画像をしばらく覚えておくことがあります。画像を作り直しても、すでに共有した投稿のカードはすぐには変わらないことがあるので、そういうものだと思っておきましょう。

まとめ

アイキャッチを描いているのは、決めておいた型に記事のタイトルを差し込む Satori と sharp です。その2つを Astro のビルドのときに動かしているので、画像を返すファイルを1つ置くだけで、記事の数だけアイキャッチが自動でできあがります。

作ったあとは、記事を書くだけで画像がついてくるようになります。画像編集ソフトを開く時間を、記事を書く時間に回せます。

Cloudflare でサイトを公開するところから始めたい場合は、こちらの記事からどうぞ。

最初から読むClaude Code と Cloudflare でサンプルサイトを公開するまでClaude Code への1つの指示でサイトを生成し、GitHub 経由で Cloudflare Workers へ無料公開する最短手順と設定の勘所を解説します。