}

ブログのサムネが表示されない?OGPタグで一発解決した話

日常パソコン部
この記事は約33分で読めます。

ブログ村やX(旧Twitter)、BlueSkyに記事をシェアしたとき、「あれ?サムネイル画像が出てない…」って経験ありませんか?

よくは覚えていないのですが、WordpressのあるバージョンからサムネがAvif形式に対応して、それからおかしくなったんですよね。

私もずっとこれに悩まされてました。せっかくいい記事を書いても、リンクだけがポツンと表示されるだけで、なんだか寂しいんですよね。クリック率も明らかに下がるし。

最初は「Avif形式が原因かも?」と思って画像形式を変えたりしたんですが、それでも解決せず。で、色々調べた結果、やっと犯人を見つけました。それが「OGPタグ」の設定不足だったんです。

今回は、同じ悩みを抱えている人のために、OGPタグって何なのか、どうやって設定するのかを、実際に私がやった方法と一緒にシェアします。正直、最初は「function.phpって触って大丈夫なの?」ってビビってましたけど(笑)、意外と簡単だったので安心してください。

スポンサーリンク

サムネが表示されない原因はOGPタグの欠如だった

結論から言うと、OGPタグをfunction.phpに追加したら、ちゃんとサムネイルが表示されるようになりました。

OGPタグっていうのは、簡単に言えば「SNSやブログサービスに対して、この記事のタイトルはこれで、画像はこれだよ!」って教えてあげるための目印みたいなものです。これがないと、シェアしたときに相手側のシステムが「どの画像を表示すればいいの?」って迷っちゃうんですね。

私の場合、Avif形式の画像を使っていたことも関係していたみたいですが、根本的な原因はOGPタグが正しく設定されていなかったことでした。このタグをWordPressのfunction.phpファイルに追加することで、ブログ村でもXでもBlueSkyでも、きちんとサムネイルが表示されるようになったんです。

OGPタグがないとサムネが表示されない仕組み

なんでOGPタグが必要なのか、ちょっと仕組みを説明しますね。

SNSやブログサービスって、リンクが投稿されたときに「このページにはどんな情報があるんだろう?」って自動的にチェックしに来るんです。このとき参照しているのが、HTMLの中に埋め込まれた「メタタグ」と呼ばれる情報。その中でも特に重要なのがOGP(Open Graph Protocol)タグです。

OGPタグには以下のような情報を含めることができます:

  • タイトル:記事のタイトル
  • 説明文:記事の概要
  • 画像URL:サムネイルとして表示したい画像のURL
  • ページのURL:記事の正式なURL

これらがちゃんと設定されていれば、ブログ村やX、BlueSkyなどのプラットフォームが「あ、この画像を使えばいいんだな」って理解してくれるわけです

逆に、OGPタグが設定されていないと:

  • サムネイル画像が表示されない
  • タイトルがおかしく表示される
  • 説明文が勝手に抜粋される(変な部分が切り取られることも)

といった問題が起きます。私の場合、まさにこの「サムネイル画像が表示されない」状態だったんですね。

ちなみに、Avif形式の画像が原因じゃないの?って思うかもしれませんが、Avif自体は最近のブラウザやSNSでも対応が進んでいるので、OGPタグさえ正しく設定されていれば問題なく表示されることが多いです。

ただ、念のためJPEGやPNGも試してみるのはアリかも。私は正直めんどくさいと思ったのでAvifのまま放置しています(笑)。サムネイルをアップロードする時点で勝手にWordPressがAvif形式にしちゃうんですよね。

function.phpにOGPタグを追加する具体的な方法

じゃあ、実際にどうやって設定するのか。ここからが本題です。

WordPressを使っている場合、function.phpというファイルにコードを追加することで、全ページに自動的にOGPタグを出力できます。

設定手順

  1. WordPressの管理画面にログイン
  2. 「外観」→「テーマファイルエディター」をクリック
  3. 右側のファイル一覧から「function.php」を選択
  4. 以下のコードをファイルの最後に追加
/**
 * ========================================
 * OGP設定(AVIF対応・完全版)
 * ========================================
 */

// デバッグモード(問題解決後はfalseに変更)
define('OGP_DEBUG_MODE', true);

// デフォルトOGP画像(JPEG形式で用意してください)
define('OGP_DEFAULT_IMAGE', 'https://shirakawaroom.com/wp-content/uploads/default-ogp.jpg');


/**
 * AVIF画像をJPEG画像に変換する関数
 */
function convert_avif_to_jpeg($image_url) {
    // AVIF以外はそのまま返す
    $extension = strtolower(pathinfo($image_url, PATHINFO_EXTENSION));
    if ($extension !== 'avif') {
        return $image_url;
    }
    
    if (OGP_DEBUG_MODE) {
        echo "<!-- OGP: AVIF detected, attempting conversion -->\n";
    }
    
    // URLからファイルパスに変換
    $upload_dir = wp_upload_dir();
    $avif_path = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $image_url);
    
    // ファイルが存在しない場合
    if (!file_exists($avif_path)) {
        if (OGP_DEBUG_MODE) {
            echo "<!-- OGP: AVIF file not found on server -->\n";
        }
        return $image_url;
    }
    
    // JPEG版のパスとURLを生成
    $jpeg_path = preg_replace('/\.avif$/i', '.jpg', $avif_path);
    $jpeg_url = preg_replace('/\.avif$/i', '.jpg', $image_url);
    
    // すでにJPEG版が存在する場合
    if (file_exists($jpeg_path)) {
        if (OGP_DEBUG_MODE) {
            echo "<!-- OGP: JPEG version already exists -->\n";
        }
        return $jpeg_url;
    }
    
    // ========================================
    // JPEG生成を試みる
    // ========================================
    
    // 方法1:GDライブラリ(PHP 8.1以降)
    if (function_exists('imagecreatefromavif') && function_exists('imagejpeg')) {
        $image = @imagecreatefromavif($avif_path);
        
        if ($image !== false) {
            $result = imagejpeg($image, $jpeg_path, 90);
            imagedestroy($image);
            
            if ($result && file_exists($jpeg_path)) {
                if (OGP_DEBUG_MODE) {
                    echo "<!-- OGP: Successfully converted using GD -->\n";
                }
                return $jpeg_url;
            }
        }
    }
    
    // 方法2:ImageMagick
    if (extension_loaded('imagick')) {
        try {
            $imagick = new Imagick($avif_path);
            $imagick->setImageFormat('jpeg');
            $imagick->setImageCompressionQuality(90);
            $imagick->writeImage($jpeg_path);
            $imagick->clear();
            $imagick->destroy();
            
            if (file_exists($jpeg_path)) {
                if (OGP_DEBUG_MODE) {
                    echo "<!-- OGP: Successfully converted using ImageMagick -->\n";
                }
                return $jpeg_url;
            }
        } catch (Exception $e) {
            if (OGP_DEBUG_MODE) {
                echo "<!-- OGP: ImageMagick error - " . esc_html($e->getMessage()) . " -->\n";
            }
        }
    }
    
    // 方法3:WordPressイメージエディタ
    $image_editor = wp_get_image_editor($avif_path);
    
    if (!is_wp_error($image_editor)) {
        $saved = $image_editor->save($jpeg_path, 'image/jpeg');
        
        if (!is_wp_error($saved) && file_exists($jpeg_path)) {
            if (OGP_DEBUG_MODE) {
                echo "<!-- OGP: Successfully converted using WP Image Editor -->\n";
            }
            return $jpeg_url;
        }
    }
    
    // 変換失敗
    if (OGP_DEBUG_MODE) {
        echo "<!-- OGP: WARNING - Could not convert AVIF to JPEG -->\n";
        echo "<!-- OGP: Server does not support AVIF conversion -->\n";
        echo "<!-- OGP: Please manually upload JPEG version -->\n";
    }
    
    return $image_url;
}


/**
 * 説明文を取得する関数
 */
function get_ogp_description($post) {
    $description = '';
    
    // 優先順位1:記事の抜粋
    $excerpt = get_the_excerpt($post->ID);
    if (!empty($excerpt)) {
        $description = $excerpt;
        
        if (OGP_DEBUG_MODE) {
            echo "<!-- Description source: excerpt -->\n";
        }
    }
    // 優先順位2:本文から自動生成
    else {
        $content = $post->post_content;
        
        // HTMLタグとショートコードを削除
        $content = strip_shortcodes($content);
        $content = strip_tags($content);
        
        // 余分な空白を1つのスペースに置換
        $content = preg_replace('/\s+/u', ' ', $content);
        $content = trim($content);
        
        if (!empty($content)) {
            // 120文字で切り取る
            if (mb_strlen($content) > 120) {
                $description = mb_substr($content, 0, 120);
                
                // 句読点で自然に切る処理
                $last_period = mb_strrpos($description, '。');
                $last_comma = mb_strrpos($description, '、');
                $last_point = max($last_period, $last_comma);
                
                // 句読点が後半にあれば、そこで切る
                if ($last_point !== false && $last_point > 80) {
                    $description = mb_substr($description, 0, $last_point + 1);
                } else {
                    $description .= '...';
                }
            } else {
                $description = $content;
            }
            
            if (OGP_DEBUG_MODE) {
                echo "<!-- Description source: content -->\n";
            }
        }
    }
    
    // 優先順位3:サイトのキャッチフレーズ
    if (empty($description)) {
        $description = get_bloginfo('description');
        
        if (OGP_DEBUG_MODE) {
            echo "<!-- Description source: site tagline -->\n";
        }
    }
    
    // 優先順位4:デフォルトテキスト
    if (empty($description)) {
        $description = get_bloginfo('name') . 'の記事です。';
        
        if (OGP_DEBUG_MODE) {
            echo "<!-- Description source: default text -->\n";
        }
    }
    
    // HTMLエンティティをデコード
    $description = html_entity_decode($description, ENT_QUOTES, 'UTF-8');
    
    // 制御文字を削除
    $description = preg_replace('/[\x00-\x1F\x7F]/u', '', $description);
    
    // 前後の空白を削除
    $description = trim($description);
    
    if (OGP_DEBUG_MODE) {
        echo "<!-- Description length: " . mb_strlen($description) . " characters -->\n";
        echo "<!-- Description: " . esc_html(mb_substr($description, 0, 60)) . "... -->\n";
    }
    
    return $description;
}


/**
 * OGPタグを出力する関数(統合デバッグ版)
 */
function output_custom_ogp_tags() {
    // 個別記事と固定ページ以外はスキップ
    if (!is_single() && !is_page()) {
        return;
    }
    
    global $post;
    
    if (!isset($post) || !is_object($post)) {
        return;
    }
    
    if (OGP_DEBUG_MODE) {
        echo "\n<!-- ========================================= -->\n";
        echo "<!-- OGP Debug Information                     -->\n";
        echo "<!-- ========================================= -->\n";
        echo "<!-- Post ID: " . $post->ID . " -->\n";
    }
    
    // タイトル取得
    $title = get_the_title();
    if (empty($title)) {
        $title = get_bloginfo('name');
    }
    $title = strip_tags($title);
    
    if (OGP_DEBUG_MODE) {
        echo "<!-- Title: " . esc_html($title) . " -->\n";
    }
    
    // 説明文取得
    $description = get_ogp_description($post);
    
    // ========================================
    // 画像URL取得(統合版)
    // ========================================
    $image_url = '';
    $final_image_url = '';
    
    if (has_post_thumbnail()) {
        $thumbnail_id = get_post_thumbnail_id();
        $image_data = wp_get_attachment_image_src($thumbnail_id, 'large');
        
        if ($image_data && !empty($image_data[0])) {
            $original_url = $image_data[0];
            
            if (OGP_DEBUG_MODE) {
                echo "<!-- Original Image URL: " . esc_html($original_url) . " -->\n";
                $ext = strtolower(pathinfo($original_url, PATHINFO_EXTENSION));
                echo "<!-- Image Format: " . esc_html($ext) . " -->\n";
            }
            
            // AVIF→JPEG変換を試みる(この関数内でデバッグ情報も出力される)
            $converted_url = convert_avif_to_jpeg($original_url);
            
            if (OGP_DEBUG_MODE) {
                echo "<!-- Converted Image URL: " . esc_html($converted_url) . " -->\n";
            }
            
            // 変換結果を確認
            $converted_ext = strtolower(pathinfo($converted_url, PATHINFO_EXTENSION));
            
            if ($converted_ext === 'avif') {
                // まだAVIFの場合は警告
                if (OGP_DEBUG_MODE) {
                    echo "<!-- WARNING: Still AVIF format after conversion attempt! -->\n";
                    echo "<!-- Will use default image for SNS compatibility -->\n";
                }
                $final_image_url = OGP_DEFAULT_IMAGE;
            } else {
                // JPEG/PNG/WebP等に変換成功
                $final_image_url = $converted_url;
            }
        }
    } else {
        if (OGP_DEBUG_MODE) {
            echo "<!-- No featured image set -->\n";
        }
    }
    
    // デフォルト画像を使用
    if (empty($final_image_url)) {
        $final_image_url = OGP_DEFAULT_IMAGE;
        
        if (OGP_DEBUG_MODE) {
            echo "<!-- Using default image -->\n";
        }
    }
    
    if (OGP_DEBUG_MODE) {
        echo "<!-- Final Image URL: " . esc_html($final_image_url) . " -->\n";
        
        // 最終URLの形式を確認
        $final_ext = strtolower(pathinfo($final_image_url, PATHINFO_EXTENSION));
        echo "<!-- Final Image Format: " . esc_html($final_ext) . " -->\n";
        
        if ($final_ext === 'avif') {
            echo "<!-- !!!!! ERROR: Final image is AVIF! This will NOT work on SNS! !!!!! -->\n";
        }
    }
    
    // URL取得
    $url = get_permalink();
    $site_name = get_bloginfo('name');
    
    // ========================================
    // 出力直前の最終チェック
    // ========================================
    $output_image_url = $final_image_url;
    $output_ext = strtolower(pathinfo($output_image_url, PATHINFO_EXTENSION));
    
    // 万が一まだAVIFなら強制的にデフォルト画像に差し替え
    if ($output_ext === 'avif') {
        $output_image_url = OGP_DEFAULT_IMAGE;
        
        if (OGP_DEBUG_MODE) {
            echo "<!-- EMERGENCY OVERRIDE: Forced to use default image -->\n";
        }
    }
    
    if (OGP_DEBUG_MODE) {
        echo "<!-- Output Image URL (what will actually be in meta tags): " . esc_html($output_image_url) . " -->\n";
        echo "<!-- ========================================= -->\n\n";
    }
    
    // OGPタグ出力
    ?>
<!-- Open Graph Protocol Tags -->
<meta property="og:title" content="<?php echo esc_attr($title); ?>" />
<meta property="og:type" content="article" />
<meta property="og:url" content="<?php echo esc_url($url); ?>" />
<meta property="og:image" content="<?php echo esc_url($output_image_url); ?>" />
<meta property="og:site_name" content="<?php echo esc_attr($site_name); ?>" />
<meta property="og:description" content="<?php echo esc_attr($description); ?>" />

<!-- Twitter Card Tags -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="<?php echo esc_attr($title); ?>" />
<meta name="twitter:description" content="<?php echo esc_attr($description); ?>" />
<meta name="twitter:image" content="<?php echo esc_url($output_image_url); ?>" />

<?php
    // 実際に出力された内容を確認(デバッグ用)
    if (OGP_DEBUG_MODE) {
        echo "<!-- Verification: OGP image tag contains: " . esc_html($output_image_url) . " -->\n";
        $verify_ext = strtolower(pathinfo($output_image_url, PATHINFO_EXTENSION));
        
        if ($verify_ext === 'avif') {
            echo "<!-- !!!!! CRITICAL ERROR: AVIF was output to OGP tags! !!!!! -->\n";
            echo "<!-- SNS will NOT display this thumbnail! -->\n";
        } else {
            echo "<!-- SUCCESS: Non-AVIF image (" . esc_html($verify_ext) . ") output to OGP tags -->\n";
        }
    }
}

// wp_headフックに登録
add_action('wp_head', 'output_custom_ogp_tags', 5);


/**
 * 今後アップロードする画像をJPEG形式に強制する
 */
add_filter('image_editor_output_format', function($formats) {
    $formats['image/avif'] = 'image/jpeg';
    $formats['image/webp'] = 'image/jpeg';
    return $formats;
}, 10, 1);


/**
 * 管理画面でAVIF画像の警告を表示
 */
add_action('admin_notices', function() {
    $screen = get_current_screen();
    if (!$screen || $screen->base !== 'post') {
        return;
    }
    
    global $post;
    
    if (!$post || !has_post_thumbnail($post->ID)) {
        return;
    }
    
    $thumbnail_id = get_post_thumbnail_id($post->ID);
    $image_url = wp_get_attachment_url($thumbnail_id);
    $extension = strtolower(pathinfo($image_url, PATHINFO_EXTENSION));
    
    if ($extension === 'avif') {
        echo '<div class="notice notice-warning is-dismissible">';
        echo '<p><strong>⚠️ 警告:</strong>アイキャッチ画像がAVIF形式です。';
        echo 'SNS(ブログ村、X、BlueSky)でサムネイルが表示されません。';
        echo '<br>JPEG形式の画像に変更することを推奨します。</p>';
        echo '</div>';
    }
});

これを保存すると、各記事ページのHTMLに自動的にOGPタグが埋め込まれます。

デバッグ情報の見方

ソースを表示して、以下のような情報が出ているか確認してください。

<!-- ========================================= -->
<!-- OGP Debug Information                     -->
<!-- ========================================= -->
<!-- Post ID: 14572 -->
<!-- Title: ブログのサムネが表示されない?OGPタグで一発解決した話 -->
<!-- Description source: excerpt -->
<!-- Description length: 114 characters -->
<!-- Description: ブログ村やX(旧Twitter)、BlueSkyに記事をシェアしたとき、「あれ?サムネイル画像が出てない…」って経験あり... -->
<!-- Original Image URL: https://shirakawaroom.com/wp-content/uploads/2025/10/Copilot_20251019_221634-1024x683.avif -->
<!-- Image Format: avif -->
<!-- OGP: AVIF detected, attempting conversion -->
<!-- OGP: JPEG version already exists -->
<!-- Converted Image URL: https://shirakawaroom.com/wp-content/uploads/2025/10/Copilot_20251019_221634-1024x683.jpg -->
<!-- Final Image URL: https://shirakawaroom.com/wp-content/uploads/2025/10/Copilot_20251019_221634-1024x683.jpg -->
<!-- Final Image Format: jpg -->
<!-- Output Image URL (what will actually be in meta tags): https://shirakawaroom.com/wp-content/uploads/2025/10/Copilot_20251019_221634-1024x683.jpg -->
<!-- ========================================= -->

「Description」にページの説明が書かれていたり、「Final Image URL」にJPEGの画像が正しく設定されていれば成功です!

もしエラーメッセージが表示された場合は、サーバーがAVIF変換に対応していないため、手動でJPEG版をアップロードする必要があります。

試してみて、成功しなかった場合は、FTP経由でJPEGファイルを1つずつアップしていきましょう。

テーマ:Cocoon を使用されている方は、Cocoon設定のOGP設定から、「OGPタグを挿入する」のチェックを外してみましょう。きっとうまく行くはずです。私もこれがうまくいかない原因のひとつとなっていました。

動作確認の方法

設定したら、ちゃんと動いているか確認しましょう。

確認方法手順
Facebook シェアデバッガーhttps://developers.facebook.com/tools/debug/ にアクセスして記事URLを入力
Twitter Card Validatorhttps://cards-dev.twitter.com/validator にアクセスして記事URLを入力
ページのソースを表示ブラウザで記事を開いて右クリック→「ページのソースを表示」→<meta property="og:で検索

私の場合、Facebookのシェアデバッガーで確認したら、最初は古いキャッシュが残ってて「あれ?まだ表示されない…」ってなったんですけど、「もう一度スクレイピング」ボタンを押したらちゃんと新しい画像が表示されました。このキャッシュ問題、結構ハマるポイントなので注意してください。

注意点とトラブルシューティング

1. function.phpの編集は慎重に! function.phpはWordPressの動作を制御する重要なファイルなので、変なところにコードを入れたり、タイポ(誤字)があったりすると、サイト全体が真っ白になることがあります(実際、私も一度やらかしました…笑)。

対策

  • 編集前に必ずバックアップを取る
  • 子テーマを使う(親テーマを直接編集しない)
  • FTPでアクセスできる環境を用意しておく(万が一のときに復旧できるように)

2. 画像サイズに注意 OGP画像は推奨サイズがあります。X(Twitter)だと1200×630ピクセルが理想的。大きすぎても小さすぎてもうまく表示されないことがあるので、できれば適切なサイズにリサイズしておきましょう。まあ、私はたまにサイズ無視して適当にアップロードしちゃうこともありますけど(笑)。

3. キャッシュ問題 SNS側でキャッシュが残っていると、OGPタグを設定しても古い情報が表示され続けることがあります。上で紹介したデバッガーツールでキャッシュをクリアしましょう。

4. プラグインとの競合 Yoast SEOやAll in One SEOなどのSEOプラグインを使っている場合、すでにOGPタグが出力されている可能性があります。その場合は、function.phpに追加したコードが重複して問題を起こすこともあるので、プラグインの設定を確認してください。

最後に

ブログ村やX、BlueSkyでサムネイルが表示されない問題、本当にモヤモヤしますよね。でも、OGPタグをfunction.phpに追加するだけで、びっくりするくらいあっさり解決します。

今回紹介した方法は、WordPressを使っている人なら誰でもできる方法です。コードをコピペして、デフォルト画像のURLだけ自分のものに変えればOK。所要時間は10分もかかりません。

私自身、最初は「function.phpって触って大丈夫なの?」って不安だったんですけど、バックアップさえ取っておけば全然怖くなかったです。むしろ、こういう設定をいじれるようになると、WordPressがもっと楽しくなりますよ。

サムネイルがちゃんと表示されるようになると、SNSでのクリック率も上がるし、記事の見栄えも全然違います。ぜひ試してみてください!

もし「うまくいかない…」ってときは、まずキャッシュのクリアと、SEOプラグインとの競合をチェックしてみてくださいね。それでもダメなら、コードのタイポがないか確認を。大体この辺で解決します。

それでは、快適なブログライフを!

白川秋
白川秋

ではでは、参考までに。

コメント

タイトルとURLをコピーしました