X(旧Twitter)やFacebookにURLを貼ると、タイトル・説明文・画像がカード状にまとまって表示されることがある。あれは偶然や自動生成ではなく、ページ側が用意したOGP(Open Graph Protocol)というメタデータを、SNS側のクローラが読み取って組み立てている。前回のJSON-LDに続き、今回は同じく <head> に置かれるOGPについて、何が表示を決めているのかを、このブログ(wpmm.jp/blog と en.wpmm.jp/blog)の実装を例に整理する。
補足: OGPはFacebookが2010年に提唱した仕様で、名前の通り「ページをグラフ(ノードの集合)の一部として扱う」という発想に由来する。現在はFacebook以外の多くのSNS・チャットアプリ(X、LINE、Slackなど)が同じメタタグを読み取ってカード表示に使っている、事実上の業界標準になっている。
カード表示の正体は <meta> タグ
OGPの実体は特別な技術ではなく、<head> 内に並ぶ <meta property="og:..."> タグである。最低限、次の4つが揃っていればカードとして成立する。
| プロパティ | 役割 |
|---|---|
og:title |
カードに表示するタイトル |
og:description |
カードに表示する説明文 |
og:image |
カードに表示する画像のURL |
og:url |
このページの正規URL(シェア元として記録される) |
SNS側のクローラは、URLが投稿・送信されたタイミングでそのページを取得し、これらのタグの値を読み取ってカードを組み立てる。ページの見た目(実際にブラウザで表示されるレイアウト)とは完全に別の、カード専用の情報という位置づけになる。
og:type — このページは「何」なのか
og:type は、ページの種類を宣言するプロパティである。ブログ記事なら article、トップページのような一般的なページなら website を指定する。
<meta property="og:type" content="article" />
article を指定すると、article:published_time(公開日時)や article:author(著者)といった記事特有の追加プロパティも合わせて出力できる。このブログでは、個別記事ページで og:type="article"、それ以外(トップページ・アーカイブ・検索結果など)で og:type="website" を出し分けている。
画像サイズの指定が地味に効く
og:image に加えて、og:image:width / og:image:height を明示しておくと、クローラが画像を取得する前にレイアウトを確定できるため、カードの組み立てが安定しやすくなる。推奨サイズは各SNSでおおむね近く、横長の 1200×630 前後が広く使われている。このブログのOGP画像もこのサイズで統一している。
<meta property="og:image" content="https://wpmm.jp/blog/wp-content/uploads/ogp/post-52-ja-20260922010000.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
X向けには、さらに twitter:card(summary_large_image を指定すると大きい画像付きカードになる)と twitter:title / twitter:description / twitter:image も別途用意しておく。X は独自の twitter: プレフィックスのタグを優先して読むため、OGPだけでは画像が小さいカードになったり、想定と違う表示になったりすることがある。
画像URLが1つに決まらない理由 — このブログの4段階フォールバック
このブログでは、記事にアイキャッチ画像を設定していない場合でも、記事タイトルを焼き込んだ画像を自動生成してOGPに使っている。og:image に何を出すかは、次の優先順で決まる。
- 記事にアイキャッチ画像が明示的に設定されていれば、それを最優先で使う
- 個別記事で、事前に生成済みのOGP画像(ディスクキャッシュ)があれば、そのクエリなしの静的URLを使う
- 個別記事だが、キャッシュがまだ生成されていなければ、その場で画像を生成するエンジンをクエリパラメータ付きURLで指す(フォールバック)
- 記事ページ以外(トップ・アーカイブ・404など)は、サイト共通のデフォルト画像を使う
// 説明用に簡略化した疑似コード
function wpmm_blog_ogp_image_url() {
if ( is_singular() && has_post_thumbnail() ) {
return get_the_post_thumbnail_url(); // 1) アイキャッチ優先
}
if ( is_singular( 'post' ) ) {
$cached = find_cached_ogp_png( get_the_ID() );
if ( $cached ) {
return $cached; // 2) 静的キャッシュURL
}
return generator_url_with_query(); // 3) 動的生成へフォールバック
}
return $default_ogp_image_url; // 4) サイト共通デフォルト
}
なぜ2番と3番を分けているかというと、クエリパラメータ付きのURL(動的URL)は、SNS側のクローラによっては画像として正しく扱われないことがあるためである。実際にこのブログでも、動的URLのままX投稿を行ったところ、画像が読み込まれず文字だけの小さいカードで固定表示される事故が過去に起きている。対処として、記事の公開・更新時にサーバー内部からその場で1回画像生成エンジンを呼び出し、生成結果をディスクにファイルとして保存しておく仕組みを追加した。これにより、SNSにシェアされる時点では既にクエリなしの静的PNGファイルが存在し、og:image はその静的URLを返せる状態になる。動的URLは、何らかの理由でキャッシュ生成に失敗した場合の保険としてのみ機能する。
キャッシュと Content-Type も無関係ではない
OGP画像を配信するエンジン側では、Content-Type: image/png を明示し、Cache-Control で長めのキャッシュ期間を指定している。SNS側のクローラ自身も取得結果を一定期間キャッシュする仕様のことが多く、一度失敗した取得結果(画像なしカード)がしばらく引きずられるケースがある。前々回の記事で扱ったHTTPキャッシュヘッダーの考え方は、OGP画像の配信でもそのまま当てはまる。
og:url は「シェア元」を固定する役割
og:url には、末尾のスラッシュや余計なクエリを含まない正規のURLを入れる。ここが揺れていると、同じ記事なのにURL違いとして複数のカード・複数の「いいね」数に分裂して集計されることがある。canonical URLと同じ値を使うのが安全である。
// アーカイブ・404等はクエリ文字列を取り除いてから使う
$ogp_url = strtok( $_SERVER['REQUEST_URI'], '?' );
出力後に確認するには
OGPタグも、hreflangやJSON-LDと同様に画面には現れず、見落としに気づきにくい。確認方法はいくつかある。
- ブラウザでページのソースを開き、
og:で始まるタグが正しい値で出力されているか見る curlでog:imageのURLだけを取り出し、クエリパラメータの有無を確認する(動的URLのままになっていないか)- 各SNSが提供しているシェアプレビュー確認ツールで、実際のカード表示を見る
このブログでは、記事の公開後にSEO確認用のスクリプト audit.py で og:type / og:title / og:description / og:url / og:image が一通り出力されているかを機械的にチェックしている。
よくある落とし穴
og:imageが動的URL(クエリ付き)のまま: SNSクローラの取得に失敗しやすく、画像なしカードの原因になりやすいog:image:width/heightを書いていない: 必須ではないが、レイアウト確定が遅れてカードが不安定になりやすいog:urlにクエリ文字列が混入する: 同じ記事が複数URLとして分裂集計される- 一度失敗したカードをSNS側が長くキャッシュする: 修正後もすぐには反映されないことがある。再クロールの公式な強制手段がない場合は、別URL(クエリ付きなど)で再シェアする、といった回避策が必要になる
まとめ
OGPは、<meta property="og:..."> という形でページに埋め込まれた、SNSシェア専用の要約情報である。og:title / og:description / og:image / og:url の4点が土台になり、og:type でページの種類を、画像サイズの明示でレイアウトの安定性を補強する。画像URLは「静的か動的か」でSNSクローラからの見え方が変わるため、可能な限り事前生成した静的URLを優先し、動的生成は失敗時の保険にとどめておくと、カード表示の事故を防ぎやすくなる。