ZolaブログにOGP画像自動生成を組み込んだ
静的サイトジェネレーターZolaとtabiテーマの組み合わせで 運用しているこのブログに、OGP画像の自動生成を導入しました。 Zolaに組み込みの仕組みがない中でどう対応したか、 検討から導入までの経緯を記録します。 このブログはZolaとtabiテーマで運用しています。 Next.jsから移行して10日ほど経ち、 基本的な運用は安定してきましたが、 OGP画像が未対応のままでした。 SNSでリンクを共有したときに 表示される画像がないのは寂しいものです。 対応の顛末を記録します。 OGP(Open Graph Protocol)は、 URLを共有した際にタイトルや説明文、 画像をプレビュー表示するための仕組みです。 BlueskyやSlack、LINEなど、 いまやほとんどのサービスが対応しています。 OGP画像が未設定の場合、 リンクはテキストだけの素っ気ない表示になるか、 サイト共通のデフォルト画像が使われます。 このブログでは後者の状態でした。 どの記事を共有しても同じ画像が出るので、 タイムラインで見かけても 何の記事か判別できません。 記事ごとにタイトル入りの画像を用意すれば、 共有リンクだけで内容が伝わります。 Zolaの画像処理機能は tabiテーマ側には tabiの作者であるwelpo氏も、 自身のブログ (osc.garden) でこの課題に取り組んでいます。 氏のアプローチは このブログでも独自の対応が必要でした。 スクリーンショット方式はビルドの複雑さから 見送り、別のアプローチを探ることにしました。 OGP画像の生成方式には、大きく2つの方向があります。 フレーム方式はすべての記事で統一感が出ますが、 記事ごとの個性は薄くなります。 カバー画像方式は記事の内容が視覚的に伝わる 反面、画像のない記事には対応できません。 このブログには既存記事が200近くあり、 写真やスクリーンショットを含む記事と、 テキストだけの記事が混在しています。 どちらか一方の方式では、 必ずカバーできない記事が出てきます。 そこで両者のハイブリッド方式を採りました。 記事フォルダにOGPの背景として十分なサイズの 画像があればそれを使い、 なければサイト共通のデフォルト画像を使い、記事タイトルを合成する一括生成スクリプトを作成しました。 カバー画像のある記事は記事固有の見た目になり、 画像がない記事もブログとしての統一感を保てます。 どの記事に画像があるかを人が判断する必要もなく、 スクリプトがサイズを見て自動で振り分けます。 Zolaでは記事と同じフォルダに画像を 配置できます。 そこで、OGP画像は こうすることで、各記事のfrontmatterからは常に同じ相対パスでOGP画像を参照できます。 既存記事については、同じfrontmatterを追加するだけで対応できます。 frontmatterに機械的に 新規記事についてはNeovimのテンプレート機能で frontmatterに 今後の運用でも、今回作成した一括生成スクリプトを活用します。 このスクリプトは、既にOGP画像がある記事はスキップするため、 新しい記事を書いたらこのスクリプトを実行するだけで新規記事の分だけOGP画像を生成してくれます。 そして記事のテンプレートにもうすでにOGP画像の指定がfrontmatterとして設定されています。 もちろん、今後ベースとなったフレーム画像が気にくわなければ各記事フォルダの 200記事すべてにOGP画像が生成され、 SNS共有時の見栄えが改善されました。 今回は過度に自動化せず、 新規記事については毎回手動で OGP画像生成スクリプトを実行する 運用にしています。 実装の技術的な詳細は Zennに投稿しています。OGP画像がなぜ必要か


Zola + tabiテーマでのOGP画像の扱い
resize_image() による リサイズ、クロップ、フォーマット変換に 限られます。 テキストを画像に描画する機能はなく、 OGP画像の自動生成は備わっていません。social_media_images.html という テンプレートがあり、frontmatterの [extra] social_media_card で 指定された画像パスから og:image 等の メタタグを出力します。 ただし画像ファイルへの参照を担うだけで、 画像そのものは生成しません。 ファイルは事前に用意しておく必要があります。shot-scraper で ページのスクリーンショットを撮影し、 OGP画像にするというものです。 ページの見た目がそのまま プレビューになるため、 デザインの一貫性は高いです。 一方で、Zolaの開発サーバーを起動した上で スクリーンショットを撮る必要があり、 ビルドプロセスが複雑になります。選んだアプローチ
OGP画像生成方式
OGP画像配置戦略
ogp.webp という固定のファイル名で 記事フォルダに出力することとしました。social_media_card = "ogp.webp" を 追加するスクリプトを作成して一括で対応しました。social_media_card = "ogp.webp" を最初から埋め込んでおくことで対応できてしまいます。運用について
ogp.webpを削除すれば再作成されます。導入してみて
zola serve でプレビューを確認する際、 OGP画像がないとエラーになるのが 微妙にストレスなので、 いずれビルドプロセスへの組み込みも 検討したいところです。