発生した事象:Astroブログでリンクの「カード」が出ない

Astro で構築した本ブログの記事を Discord や X (旧Twitter) に投稿した際、期待していたリッチなプレビュー(OGP画像の表示)が行われず、ただのテキストリンクになってしまう問題に直面しました。

前回の記事でメタタグの実装を行ったはずですが、なぜ外部サービスから認識されないのか。調査の結果、Astro 環境における「URLの相対パス」の扱いに原因がありました。

原因:クローラーは「相対パス」を解釈できない

ブラウザで Astro サイトを閲覧している分には、/favicon.png といった相対パスでも問題なく画像が表示されます。しかし、外部のクローラー(Discordのbot等)は、サイトのドメインコンテキストを持たないため、リソースを特定するために完全な絶対URLを必要とします。

私の BaseLayout.astro の実装では、以下のように記述していました。

<!-- 改善前:Discord等では画像が見つからない -->
<meta property="og:image" content="/hero.png" />

これでは、クローラーは「どこのサーバーの /hero.png なのか」が分からず、プレビューの生成を諦めてしまいます。

解決策:Astro.url を活用した絶対URLの生成

Astroには、現在アクセスしているページの情報を取得するための便利なAPI Astro.url が用意されています。

これを利用して、画像パスやカノニカルURLを動的に絶対URLへと変換するように修正しました。

BaseLayout.astro の修正

---
// BaseLayout.astro の Frontmatter内
const { title, description, image } = Astro.props;
---
<head>
  <!-- URLを絶対パスで明示 -->
  <link rel="canonical" href={Astro.url} />
  <meta property="og:url" content={Astro.url} />
  
  <!-- 画像パスを絶対URLへ変換 -->
  <meta property="og:image" content={new URL(image || '/favicon.png', Astro.url)} />
  
  <!-- Twitter用のカード設定 -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:image" content={new URL(image || '/favicon.png', Astro.url)} />
</head>

ここで重要なのは new URL(image, Astro.url) という書き方です。これによって、image がどのようなパスであっても、現在のページのベースURLに基づいた正しい絶対URLが生成されます。

根拠としたドキュメント

今回の実装は、Astroの公式ドキュメントにある以下の仕様に基づいています。

  1. [Astro.url](https://docs.astro.build/ja/reference/api-reference/#astrourl): 現在のリクエストのURLを取得するための標準的な手段です。カノニカルURLの設定に必須とされています。
  2. [Astro.site](https://docs.astro.build/ja/reference/api-reference/#astrosite): astro.config.mjs で定義した site プロパティを参照します。ビルド時にURLが確定している環境(本番環境など)でのベースURLとして機能します。

まとめ

今回の修正によって、Discord等の外部サービスに対して「このURLが正当なソースで、この絶対パスに画像がある」という情報を不備なく伝えられるようになりました。

SEOメタタグを追加する際は、常に「外部の目線(クローラー)」でパスを捉え、絶対URLを指定することが鉄則です。