今回の「3分間フッキング」は、画像のalt属性を自動挿入するテクニックをご紹介します。
alt属性はSEOとアクセシビリティの両面で重要な要素ですが、「設定しなくても見た目には影響しない」ため後回しにされがちです。特に記事数が多いサイトでは、過去の記事まで遡って設定し直すのは現実的ではありません。
functions.phpにフックを書くだけで、過去記事の画像も含めて一括対応できます。基本編と応用編の2段階でご紹介します。
alt属性がないと何が問題なのか
まず、alt属性が設定されていない状態がSEO的にどう評価されるかを整理しておきます。
| 状態 | SEO評価 | 理由 |
|---|---|---|
alt="適切なテキスト" |
✅ 最良 | 画像の内容をGoogleが正確に理解できる |
alt=""(意図的に空) |
✅ 許容 | 装飾画像として明示的にマーク。Googleはスキップする |
| alt属性自体がない | ⚠️ 不明瞭 | Googleがファイル名やページ文脈から推測しようとする |
alt="screenshot-10" |
❌ 最悪 | 意味のないノイズとしてマイナス評価の可能性がある |
重要なのは、alt属性が「ない」状態より「意味のない文字列が入っている」状態の方がSEO的に悪いということです。alt属性がなければGoogleは文脈から推測しようとしますが、意味のない文字列が入っていると「ノイズ」として扱われる可能性があります。
つまり対策の優先順位はこうなります。
- 意味のある適切なaltを設定する(理想)
- 装飾画像には
alt=""を明示的に設定する - 少なくとも意味のない文字列を入れない
今回のフックは、基本編として「altのない画像には明示的に空(alt=””)を設定する」、応用編として「altがない・空の画像に、直前の見出しから意味のあるテキストを自動挿入する」という2段階の現実的なアプローチです。
【基本編】altが空・未設定のimgタグにalt=””を自動挿入する
まずは最もシンプルな対応として、altが未設定、または空文字になっているimgタグに alt="" を自動で付与するコードです。
Googleに「この画像は装飾画像です」と明示的に伝えることで、意味のない文字列を入れるよりも評価が安定します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
add_filter('the_content', 'wp3min_simple_auto_alt'); function wp3min_simple_auto_alt($content) { // altが存在しないimgタグにalt=""を追加 $content = preg_replace( '/<img(?![^>]*\salt=)([^>]*?)>/i', '<img alt=""$1>', $content ); // alt属性はあるが空白のみのケースも統一 $content = preg_replace( '/(<img[^>]*?)alt=["\'][\s]*["\']([^>]*?>)/i', '$1alt=""$2', $content ); return $content; } |
コードの動作確認
以下のようなパターンをすべてカバーします。
| 変換前 | 変換後 |
|---|---|
<img src="photo.jpg"> |
<img alt="" src="photo.jpg"> |
<img src="photo.jpg" alt=""> |
<img src="photo.jpg" alt="">(変更なし) |
<img src="photo.jpg" alt=" "> |
<img src="photo.jpg" alt=""> |
<img src="photo.jpg" alt="説明文"> |
<img src="photo.jpg" alt="説明文">(変更なし) |
すでにaltが設定されている画像には一切手を加えません。
【応用編】直前の見出しテキストをaltに自動挿入する
基本編の alt="" でも十分ですが、「ないよりマシ」をさらに一歩超えたい方向けの応用編です。
imgタグが書かれた段落の直前にある見出し(h1〜h6)のテキストを取得して、altとして自動挿入します。同じ見出し内に複数の画像がある場合は連番(01, 02…)で区別します。
出力イメージ
|
1 2 3 4 5 6 7 8 9 10 11 |
<!-- 変換前 --> <h3>WordPressのプラグインについて</h3> <p>WordPressのプラグインとは、・・・</p> <img src="plugin-01.jpg"> <img src="plugin-02.jpg"> <!-- 変換後 --> <h3>WordPressのプラグインについて</h3> <p>WordPressのプラグインとは、・・・</p> <img src="plugin-01.jpg" alt="WordPressのプラグインについて 01"> <img src="plugin-02.jpg" alt="WordPressのプラグインについて 02"> |
コード
HTMLを正確にパースするために、正規表現ではなく DOMDocument を使います。正規表現よりも複雑なHTMLに対して安全に動作します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
add_filter('the_content', 'wp3min_htag_auto_alt', 20); function wp3min_htag_auto_alt($content) { if (is_admin() || $content === '' || stripos($content, '<img') === false) { return $content; } if (!class_exists('DOMDocument')) { return $content; } $internal_errors = libxml_use_internal_errors(true); $dom = new DOMDocument('1.0', 'UTF-8'); // フラグメントを安全に扱うためラッパー要素で囲む $wrapper_id = 'wp3min-auto-alt-wrapper'; $html = '<div id="' . $wrapper_id . '">' . $content . '</div>'; $loaded = $dom->loadHTML( '<?xml encoding="utf-8" ?>' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ); if (!$loaded) { libxml_clear_errors(); libxml_use_internal_errors($internal_errors); return $content; } $xpath = new DOMXPath($dom); $wrapper = $xpath->query('//*[@id="' . $wrapper_id . '"]')->item(0); if (!$wrapper) { libxml_clear_errors(); libxml_use_internal_errors($internal_errors); return $content; } // alt 未設定、または空白のみの alt を対象にする $imgs = $xpath->query('.//img[not(@alt) or normalize-space(@alt)=""]', $wrapper); if (!$imgs || $imgs->length === 0) { libxml_clear_errors(); libxml_use_internal_errors($internal_errors); return $content; } $heading_counter = array(); foreach ($imgs as $img) { $heading_text = wp3min_find_previous_heading_text($img, $wrapper); if ($heading_text !== '') { if (!isset($heading_counter[$heading_text])) { $heading_counter[$heading_text] = 0; } $heading_counter[$heading_text]++; $number = str_pad((string) $heading_counter[$heading_text], 2, '0', STR_PAD_LEFT); $img->setAttribute('alt', $heading_text . ' ' . $number); } else { // 見出しが見つからない場合は装飾画像扱い $img->setAttribute('alt', ''); } } $result = ''; foreach ($wrapper->childNodes as $child) { $result .= $dom->saveHTML($child); } // 念のため、alt が boolean 属性のように出力された場合を補正 $result = preg_replace_callback( '/<img\b[^>]*>/i', function ($matches) { return preg_replace('/\balt(?!\s*=)/i', 'alt=""', $matches[0]); }, $result ); libxml_clear_errors(); libxml_use_internal_errors($internal_errors); return $result; } /** * 現在ノードより前にある、文書順で最も近い h1〜h6 のテキストを取得 */ function wp3min_find_previous_heading_text(DOMNode $node, DOMNode $root) { $current = $node; while ($current = wp3min_get_previous_node($current, $root)) { if ( $current->nodeType === XML_ELEMENT_NODE && preg_match('/^h[1-6]$/i', $current->nodeName) ) { $text = trim(preg_replace('/\s+/u', ' ', $current->textContent)); if ($text !== '') { return $text; } } } return ''; } /** * 文書順で「ひとつ前」のノードを返す */ function wp3min_get_previous_node(DOMNode $node, DOMNode $root) { if ($node->isSameNode($root)) { return null; } // まず前の兄弟へ if ($node->previousSibling) { $node = $node->previousSibling; // その兄弟の一番深い末尾の子孫まで降りる while ($node->hasChildNodes()) { $node = $node->lastChild; } return $node; } // 前の兄弟がなければ親へ $parent = $node->parentNode; if (!$parent || $parent->isSameNode($root->parentNode)) { return null; } return $parent->isSameNode($root) ? null : $parent; } |
注意点
- すでにaltが設定されている画像には一切手を加えません。既存の設定を上書きする心配はありません
- 見出しが見つからない場合は
alt=""(装飾画像扱い)を設定します。意味のない文字列が入ることはありません DOMDocumentはPHP標準搭載のため、追加ライブラリは不要です- ページビルダー(Elementor等)で出力される画像はthe_contentを経由しないため、このフックの対象外となります
実装手順
- 子テーマの
functions.phpを開きます(親テーマのfunctions.phpは編集しないこと) - 上記コードを貼り付けて保存します
- 記事ページを表示し、ブラウザの開発者ツール(Mac: Cmd + Option + I / Windows: F12)でimgタグのalt属性を確認します
データベースは書き換えずに表示時のみHTMLを加工するため、フックを外せば元の状態に戻ります。副作用が少ない安全な実装です。
フック処理によって見出しの文字列が差し込まれたイメージ

さらに上を目指すなら:AIを使ったalt自動生成
今回のフックは「ないよりマシ」から「文脈のあるalt」まで対応できますが、本当に適切なaltは画像の内容を理解した上で設定するものです。
たとえば「WordPressの管理画面のスクリーンショット」と「WordPressのロゴ画像」は、同じ見出し「WordPressとは」の下にあっても、altとしては異なるテキストが適切です。
この問題を解決するために、生成AIのAPIを活用して記事の文脈を読み、imgタグに適切なaltを提案・自動設定するWordPressプラグインを現在開発中です。
- 管理画面からalt未設定の記事を一覧表示
- 記事ごとにimgタグの数・拡張子・現在のalt設定状況を確認
- AIが前後の文章を読んで適切なaltを提案(確認してから保存)
詳細は続報でご紹介予定です。
まとめ
- alt属性は「ない」より「意味のない文字列が入っている」方がSEO的に悪い
- 基本編:altが未設定・空のimgタグに
alt=""を自動挿入。装飾画像として明示的にマークできる - 応用編:直前の見出しテキストをaltに自動挿入。過去記事の画像も一括で文脈のあるaltを設定できる
- どちらも
the_contentフィルターで実装するため、データベースを書き換えず副作用が少ない
次回の「WordPress 3分間フッキング」もお楽しみに!
WordPressサイトのSEO改善や保守サポートについては、以下のページもご参照ください。
