タグページのGoogle検索結果除外をrobots.txtのDisallowからnoindexメタタグに変更した

このブログの robots.txt は、タグページ(/posts/tag/*/archives/tag/*)を重複コンテンツ対策として検索結果から除外する目的で Disallow を長らく放置設定していました。

先日 robots.txt の役割を調べ直していて、これは Google のインデックス制御としては見当違いな使い方だったと気づきました。

Disallow はクロール(巡回)を止めるだけで、インデックス(検索結果への掲載)を止める仕組みではなかったからです。

今回は noindex メタタグへの切り替え作業をまとめておきます。

Image in a image block

はじめに

このブログは Astro 製の astro-notion-blog をベースにしていて、📄Arrow icon of a page linkCloudflare PagesでAstro 7へ気軽に上げない方がよい理由 でも触れたとおり、ちょこちょこ手を入れています。

今回見直したのは、タグ一覧ページを Google の検索結果から除外する仕組みです。

robots.txtDisallow にしておけばインデックスされないだろう」と思い込んでいたのですが、これが誤りでした。

症状 / 問題点

見直し前の robots.txt は次のような内容でした。

User-agent: *
Allow: /
Disallow: /tag/
Disallow: /posts/tag/
Disallow: /archives/tag/
Sitemap: https://notion.washo3.com/sitemap-index.xml

一見するとタグページを検索結果から締め出せているように見えます。

ただ調べていくと、Disallowクロールを禁止するだけ で、インデックスを禁止するものではない ことが分かりました。

内部リンクや外部リンクが /tag/ 配下の URL を指していると、Google はページの中身をクロールできなくても URL だけを検索結果に表示してしまうことがあります。

スニペットのない、中身の分からないインデックスがスパムっぽく見える原因になりかねません。

「知らずにやっていたのが恥ずかしい」という気持ちです。🫢

原因

さらに厄介なのは、Disallow でクロール自体をブロックすると、Googlebot はページ内の noindex メタタグを読みに行けなくなる点です。

つまり noindex を機能させたいページは、そもそも robots.txt でブロックしてはいけない、ということになります。

実際にコードを確認したところ、noindex メタタグは開発環境(isDev)でのみ出力されていて、タグページ自体には本番用の noindex 指定が入っていませんでした。

クロール制御とインデックス制御を混同していた、というのが今回の根本原因だと思っています。

flowchart LR
  A["Googlebot"] -->|"クロール許可"| B["/tag/ 配下のページ"]
  B -->|"noindexメタタグを検出"| C["検索結果から除外"]
  B -->|"ページ内リンク"| D["記事ページはクロール継続"]

修正内容 / 手順

Google 推奨の方法(ページ側に noindex メタタグを入れる)に切り替えました。

1. Layout.astro に noindex prop を追加
// 変更前
export interface Props {
  title: string
  description: string
  path: string
  ogImage: string
}

// 変更後
export interface Props {
  title: string
  description: string
  path: string
  ogImage: string
  noindex?: boolean
}

const { ..., noindex = false } = Astro.props
{isDev && <meta name="robots" content="noindex, nofollow">}
{!isDev && noindex && <meta name="robots" content="noindex, follow">}

noindex, follow にすることで、ページ自体は検索結果から除外しつつ、ページ内のリンク(記事へのリンク)は Google に辿ってもらえるようにしています。

2. タグページ側で noindex を有効化
  • src/pages/posts/tag/[tag].astro
  • src/pages/posts/tag/[tag]/page/[page].astro

の2箇所で <Layout ... noindex> のように prop を渡すよう変更しました。

src/pages/archives/tag/[tagName].astro だけは独自の HTML head を持つ実装だったため、直接

<meta name="robots" content="noindex, follow" />

を追加しています。

3. robots.txt から Disallow を削除
User-agent: *
Allow: /
Sitemap: https://notion.washo3.com/sitemap-index.xml

クロール自体はブロックせず、インデックス制御は各ページの noindex メタタグに一本化しました。

なお Disallow: /tag/ は、そもそもこのプロジェクトに該当ページが存在しないテンプレート由来の不要行だったので、あわせて整理しています。

修正後の効果

npm run dev で確認した結果は次のとおりです。

ページ 期待値 結果
/robots.txt /tag/ 系の Disallow が消えている 反映済み
/archives/tag/airtag noindex, follow 出力確認
/archives/apple-airtag(通常記事) robots メタタグなし なし

/posts/tag/*Layout.astroisDev 判定により、開発環境では常に noindex, nofollow になってしまいます。

そのため本番相当の挙動は npm run build && npm run preview でのみ確認できました。

注意点

robots.txt はセキュリティ機構ではない

「全部 Allow: / にして大丈夫か」という疑問も浮かびましたが、そもそも robots.txt は行儀の良いクローラーが自主的に守る申告に過ぎず、アクセス制御の仕組みではありません。

  • 悪意のあるボットは普通に無視してアクセスしてきます
  • むしろ Disallow: /admin/ のように書くと、非公開パスの存在を教えてしまうリスクがあります
  • 本当に守りたいものは Basic 認証やログイン必須にする、検索に出したくないだけなら noindex を使う、というのが正しい使い分けだと理解しています

このブログには管理画面や API 等の非公開ルートがないため、Allow: / で全許可しつつ noindex で検索結果を制御する今の構成で問題ないと判断しました。

効果が出るまでは時間がかかる

noindex を入れたからといって、既にインデックスされているページがすぐに検索結果から消えるわけではありません。

Google が再クロールして認識するまでは、しばらく残ったままになる気がします。

まとめ

robots.txtDisallow はクロール制御であって、インデックス制御には noindex メタタグを使う、という基本を見直すきっかけになりました。

  • Disallow だけではインデックスを防げない
  • noindex を効かせたいページは robots.txt でブロックしてはいけない
  • robots.txt はアクセス制御の仕組みではない

同様の設定で悩んでいる astro-notion-blog 利用者の参考になれば幸いです。