ページ内リンクの「#」とSEO:誤解と実装手順
長い記事の先頭に目次を置いた。各項目から #section-1 のような「#」付きのリンクで見出しへ飛ばしている。——この形で評価が分かれるのではないか、という相談が絶えません。
先に結論を書きます。公式ドキュメントを読むかぎり、心配すべきはそこではありません。手を入れる価値があるのは、そのリンクがクローラーに読める形かどうかと、見出しとリンク文言が対応しているかどうかです。クロールできるリンクの条件は、公式ドキュメントに明記があります。まずそこから確認します。
「#で評価が分散する」の根拠を確かめる
不安の出どころは、たいてい重複コンテンツの話と混ざっています。Googleの正規化(重複したURLをひとつの代表URLにまとめる処理)の説明では、重複ページが生まれる原因として、地域・端末・プロトコルの違い、サイトの機能、そして事故が挙げられています。代表URLは、もっとも内容が充実していて有用なページが選ばれ、そのURLがより定期的にインデックスに登録されます。
ここにページ内リンクの「#」は出てきません。
つまり、目次リンクを外す判断の根拠を、公式の重複統合の説明から引き出すことはできない、ということです。逆の推奨はあります。URL構造のベストプラクティスでは、長いID番号・フラグメント・アンダースコアを避け、シンプルなURL構造をつくることが勧められています。複雑なURL、過剰なパラメータ、セッションID、動的カレンダーはインデックス登録の問題を生むことがあり、URLを単純にして対処するよう案内されています。
ただし、この推奨がどの範囲のURLを指しているのかまでは、文面から読み取れません。目次から同一ページ内の位置へ飛ぶリンクを外すべきだ、と読める記述も見当たりません。
判断がつかない部分は残ります。
安全側に倒すなら、運用はこうなります。サイトマップや主要導線に載せる正規のURLでは「#」を使わない。そのうえで、記事内の目次リンクは読者の移動手段として残す。この切り分けなら、URL構造の推奨と正面から衝突する箇所はありません。
先に直すべきは「クロールできる形か」
Googleは、リンクをクロールできるのは、原則として href 属性を持つ <a> 要素(アンカー要素)である場合だけだと明示しています。それ以外の形式のリンクは、ほとんどの場合クローラーに解析・抽出されません。href を持たない <a> 要素からは、URLを確実に抽出できないとも書かれています。
ここが実装の分かれ目です。
目次をJavaScriptで動かしている実装では、href を持たない <a> に onclick でスクロール処理だけを載せている例がよくあります。画面上は問題なく動きます。人の目には見えないのに、クローラーからはリンクとして存在していない、という状態が起きます。
アンカーテキスト(リンクとして見えている文字)にも基準があります。公式には、説明的で、簡潔で、関連性のあるものにすること、と示されています。さらに、JavaScriptでアンカーテキストを挿入している場合は、URL検査ツールでレンダリング後のHTMLに実際に含まれているかを確認するよう案内されています。目次を後から差し込む実装なら、この確認は必須です。
実装の手順
上から順に実行します。既存記事の改修でも、新規テンプレートでも手順は同じです。
- 見出し要素に一意の
idを付ける。<h2 id="crawlable-links">のように、記事内で重複しない値にします。日本語をそのまま入れるとURLが長くなるので、短い英数字とハイフンにそろえるのが扱いやすいです。 - 目次側を
<a href="#crawlable-links">の形にする。onclickだけで飛ばしている箇所があれば、hrefを必ず持たせます。スムーススクロールが必要なら、hrefは残したままCSSのscroll-behavior: smooth;で実現できます。 - アンカーテキストを見出しの文言にそろえる。目次の項目名と見出しが食い違っていると、読者がどこに飛ぶのか予測できません。簡潔で関連性のある文言、という基準にも沿いません。
- URL検査ツールでレンダリング後のHTMLを見る。目次のマークアップが、
href付きの<a>としてレンダリング結果に入っているかを確認します。JavaScriptで生成している場合はここで落ちがちです。 - サイトマップと内部リンクに「#」付きURLを載せていないか確認する。ページ内移動は本文の目次だけで完結させ、正規のURLは「#」なしにそろえます。
例:5,000字の導入ガイドを直す
手順を1本通します。対象は、H2が6つある長い導入ガイドとします。
改修前は、目次が <ul> の中で <li><span onclick="scrollTo('sec2')">初期設定</span></li> という形になっていました。<a> 要素ですらありません。ブラウザ上のスクロールは動いていました。それでもクローラーから見れば、これはリンクではなく、ただのテキストです。
改修は3か所です。<span> を <a href="#initial-setup"> に置き換える。対応するH2に id="initial-setup" を付ける。onclick を削り、scroll-behavior をCSSへ移す。ここまでで、目次はレンダリング後のHTMLでも href 付きの <a> になります。
そのうえでURL検査ツールを開き、レンダリングされたHTMLに6本すべてのアンカーが出ているかを数えます。5本しか出ていなければ、1本はまだJavaScript側に残っています。数が合うまでを1回の作業の終わりにします。
検索結果のジャンプ表示は、こちらから指定できない
「セクションに移動」のような表示を狙って実装したい、という動機はよくわかります。ただ、Googleはサイトリンクについて、現時点では自動生成であり、アルゴリズムの改善を続けていて、将来的にはサイト運営者の入力を取り入れる可能性がある、と説明しています。ユーザーの検索語句に対して関連性がなければ表示しない、とも明記されています。
したがって、サイトリンクを出す・出さないの判断は検索側にあります。検索結果でページ内へ直接飛ぶ表示が、これと同じ仕組みで決まっているのかどうかは、この説明からは確定できません。少なくとも、狙って出させる前提で工数を積むのは避けたほうがよいでしょう。
できるのは、質を上げる側の手当てです。公式が挙げているサイトリンクのベストプラクティスは、ユーザーがナビゲートしやすいサイト構造にすること、重要なページへ関連する他のページからリンクすること、内部リンクのアンカーテキストをリンク先のページに対して簡潔かつ関連性のあるものにすること、コンテンツ内の繰り返しを避けること、です。
見出しと目次を整える作業は、この4つのうち後半2つに直接効きます。
点検表
改修後、記事ごとに次の観点で確認します。判定が「いいえ」なら、その行の対処に進んでください。
| 確認する点 | 判定の基準 | いいえだったときの対処 |
|---|---|---|
目次は <a href> になっているか | href を持つ <a> 要素である | span+onclick を <a href="#id"> に置き換える |
見出しに id があるか | 記事内で値が重複していない | H2/H3 に短い英数字の id を付与する |
| 目次の文言と見出しが対応しているか | 目次項目を読んで飛び先が予測できる | 見出しの文言に寄せて書き直す |
| レンダリング後にリンクが存在するか | URL検査ツールの結果に <a href> が出る | サーバー側で目次を出力する形に変更する |
| 正規のURLに「#」を混ぜていないか | サイトマップと主要導線が「#」なし | 「#」付きURLをサイトマップから除く |
一つめは、さきほどの導入ガイドがまさにこれでした。三つめは、目次を「はじめに/その1/その2」のような連番にしている記事で崩れます。四つめが落ちる記事は、たいてい二つめも同時に落ちています。JavaScriptで目次を生成する実装は、id の付与も同じスクリプトに任せていることが多いからです。
つまずきやすいところ
サーバー側のリダイレクトで、特定のセクションへ直接飛ばしたい、という要望があります。これは素直には実現できません。「#」以降の部分はブラウザ内で処理され、サーバーには送られないためです。移行時にアンカー位置まで引き継ぎたい場合は、遷移先ページ側のJavaScriptで補うか、そもそもセクションを独立したページに分けるかを検討することになります。
もう一つ。長い1ページに詰め込んだ結果、セクションごとに検索意図が違ってしまっている場合、目次の整備では解決しません。その状態は、ページを分けるかどうかの判断が先です。
内部リンク全体の設計をあわせて見直すなら、内部リンクの最適化とアンカーテキストの書き方もあわせて読んでください。JavaScriptに起因する未クロールを疑っているなら、JavaScriptのリンクがクロールされない原因が近い話題です。
出典
