こんにちは、IKKOHです!
Astroで構築したブログサイトに公開した記事を検索してみると、見覚えのあるような無いような文字化け画像とともに検索結果が表示されました。

作成した記事がせっかく検索結果に表示されても、文字化け画像とともに表示されるとリンクをクリックするのを躊躇してしまいそうです。
この文字化け画像は、OGP画像といって、ビルド時にAstroが記事のタイトル、著者、サイト名から自動で生成してくれるものですが、日本語フォントが入っていないため、文字化けした状態となっています。
日本語フォントを用いてOGP画像の生成を行うように修正していきます。
🛠️ 今回のゴール
- OGP画像に日本語フォントを使用するように修正する
- OGP画像のフォントサイズを調整する
- OGP画像の生成のみに日本語フォントを使用するように修正する
astro.config.ts の修正
AstroPaper の下記の記事にある通り、設定ファイルを修正します。
Dynamic OG image generation in AstroPaper blog posts - Issue with Non-Latin Characters
fonts: [
{
name: "Google Sans Code",
name: "Noto Sans JP",
cssVariable: "--font-google-sans-code",
provider: fontProviders.google(),
fallbacks: ["monospace"],
weights: [300, 400, 500, 600, 700],
styles: ["normal", "italic"],
formats: ["woff", "ttf"],
},
],astro.config.ts
CSS変数名
上記の通り、修正した箇所は、name のみのため、cssVariable については修正していないため、以下のファイルで参照している CSS変数名 は修正していません。CSS変数名 --font-google-sans-code をフォント名に合わせて修正する場合は、AstroPaperの記事の通り、以下のファイルでの参照箇所も修正する必要があります。
- src/pages/og.png.ts
- src/pages/posts/[…slug]/index.png.ts
OGP画像を再生成
ビルドコマンドを実行し、生成されるOGP画像を確認します。
生成されるOGP画像は dist/posts/ 配下の記事と同じディレクトリに格納されています。
npm run build

文字化けは解消しましたが、タイトルのフォントサイズが大きすぎて範囲内に収まっていません。
OGP画像のフォントサイズを調整
以下のファイルを修正してタイトルのフォントサイズを調整します。
children: [
{
type: "p",
props: {
style: {
fontSize: 72,
fontSize: 60,
fontWeight: "bold",
maxHeight: "84%",
overflow: "hidden",
},
children: props.data.title,
},
},
{
type: "div",
props: {
style: {
display: "flex",
justifyContent: "space-between",
width: "100%",
marginBottom: "8px",
fontSize: 28,
},src/pages/posts/[...slug]/index.png.ts
再度OGP画像を再生成
ビルドコマンドを実行し、生成されるOGP画像を確認します。
npm run build

タイトルが範囲内に収まるようになりました。これなら全角文字で60文字以内のタイトルであれば見切れることは無さそうです。
設定したフォントはダウンロードされて、_astro/fonts/ .astro/fonts/ node_modules/.astro/fonts/ にキャッシュされます。
サイトのOGP画像
astro.config.ts での日本語フォントの設定により、サイトのOGP画像についても文字化けが解消されています。
サイトのOGP画像は dist/og.png に格納されています。
-
対応前

-
対応後

サイトのOGP画像のフォントサイズについても調整する場合は、 og.png.ts でのフォントサイズの指定を変更します。
children: [
{
type: "div",
props: {
style: {
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
height: "90%",
maxHeight: "90%",
overflow: "hidden",
textAlign: "center",
},
children: [
{
type: "p",
props: {
style: { fontSize: 72, fontWeight: "bold" },
style: { fontSize: 60, fontWeight: "bold" },
children: config.site.title,
},
},
{
type: "p",
props: {
style: { fontSize: 28 },
children: config.site.description,
},
},
],
},
},
{
type: "div",
props: {
style: {
display: "flex",
justifyContent: "flex-end",
width: "100%",
marginBottom: "8px",
fontSize: 28,
},
children: {
type: "span",
props: {
style: { overflow: "hidden", fontWeight: "bold" },
children: new URL(config.site.url).hostname,
},
},
},
},
],src/pages/og.png.ts
- フォントサイズ調整後

OGP画像の文字化けについては解消しましたが、フォント設定はブログサイト全体の設定となっています。
そのため、各記事についても日本語フォントの設定が適用され、指定したフォントファイルは配信対象となってしまいます。
今回の日本語フォントの導入は、OGP画像の文字化け対策のためであり、各記事のフォントについては、ブラウザの標準フォントを使用したいため、フォント設定は元に戻してOGP画像の生成時のみ日本語フォントをロードするように修正します。
なお、指定した日本語フォントを各記事でも使用したい場合は、以下の手順を実施不要です。
astro.config.ts の修正(元に戻す)
fonts: [
{
name: "Noto Sans JP",
name: "Google Sans Code",
cssVariable: "--font-google-sans-code",
provider: fontProviders.google(),
fallbacks: ["monospace"],
weights: [300, 400, 500, 600, 700],
styles: ["normal", "italic"],
formats: ["woff", "ttf"],
},
],astro.config.ts
日本語フォントの取得
フォントファイルのダウンロード
Google Fonts から、日本語フォントをダウンロードします。

ダウンロードしたファイルを展開し、OGP画像生成処理で使用されている2種類の太さのフォントファイルを、以下のパスに格納します。
src/assets/fonts/
* NotoSansJP-Regular.ttf
* NotoSansJP-Bold.ttf
OGP画像生成処理でのフォントのロード処理の修正
フォントのロード処理を、上記で格納したフォントファイルを読み込むように修正します。
各記事用OGP画像生成処理
const [regularData, boldData] = await Promise.all([
fetch(experimental_getFontFileURL(regularFontPath, url)).then(res =>
res.arrayBuffer()
),
fetch(experimental_getFontFileURL(boldFontPath, url)).then(res =>
res.arrayBuffer()
),
// regularフォント
(async () => await fs.promises.readFile('src/assets/fonts/NotoSansJP-Regular.ttf'))(),
// boldフォント
(async () => await fs.promises.readFile('src/assets/fonts/NotoSansJP-Bold.ttf'))(),
]);
const svg = await satori(
{src/pages/posts/[...slug]/index.png.ts
サイト用OGP画像生成処理
const [regularData, boldData] = await Promise.all([
fetch(experimental_getFontFileURL(regularFontPath, context.url)).then(res =>
res.arrayBuffer()
),
fetch(experimental_getFontFileURL(boldFontPath, context.url)).then(res =>
res.arrayBuffer()
),
// regularフォント
(async () => await fs.promises.readFile('src/assets/fonts/NotoSansJP-Regular.ttf'))(),
// boldフォント
(async () => await fs.promises.readFile('src/assets/fonts/NotoSansJP-Bold.ttf'))(),
]);
const svg = await satori(
{src/pages/og.png.ts
再度OGP画像を再生成
ビルドコマンドを実行し、生成されるOGP画像を確認します。
npm run build
OGP画像が文字化けせずに再生成されたことを確認できれば完了です。