インデックス登録保護(本番以外の環境の安全策)
サイトのステージングやローカルのコピーがGoogleに登録されるのは、よくあるSEO上のミスであり、被害も大きくなりがちです。重複コンテンツが本来のページと競合し、非公開の環境がインデックスに残り、URL削除ツールでの後処理に何週間もかかることがあります。典型的な原因は、設定し忘れた.envだけに依存するnoindexや、デプロイ時に上書きされたrobotsルールです。
インデックス登録保護は、このミスが起きにくい仕組みを設けます。誰かが覚えておく必要のあるフラグではなく、Laravelの環境にインデックス登録の可否を連動させます。アプリが許可リスト外の環境で動くと、すべてのページにnoindex,nofollowを強制し、管理対象のrobots.txtですべてのクローラーを拒否し、seo:auditでもその状態を明示します。
無料のCore機能です。
保護が有効なときの動作
保護機能が有効で、app()->environment()がseo.indexing_guard.allowed_environmentsに含まれない場合、次の4つが自動的に実行されます。
- リゾルバーがすべてのページに
noindex,nofollowを強制します。 優先順位の全体よりも上位で適用されるため、seo_metaにページ単位で明示的に保存したrobotsの値も上書きします。 X-Robots-Tag: noindex,nofollowHTTPヘッダーを、アプリを経由するすべてのレスポンスで送信します。下のHTML以外のレスポンスを参照してください。- **
SEO::robotsTxt()->build()が、すべてを拒否するrobots.txt**とai.txtを出力します。内容は単純なUser-agent: */Disallow: /です。seo:robots-txtコマンドと、任意で有効にする動的ルートの両方が対象です。 seo:auditが目立つバナーを表示します。 レポートを読んだときに「すべてがnoindexになっている」状態を見落とさずに済みます。
許可された環境(デフォルトではproduction)では、保護機能は完全に何もしません。出力は変化せず、レンダリング結果もバイト単位で一致します。
HTML以外のレスポンス(PDF、フィード、画像)
強制されたrobotsのmetaタグが届くのは、HTMLを解析するクローラーだけです。PDF、RSS/Atomフィード、画像など、HTML以外のレスポンスには<head>がありません。そのため、保護が有効な間はグローバルミドルウェアを通じて、同じ指示をHTTPヘッダーでも送信します。
X-Robots-Tag: noindex,nofollowヘッダーとmetaタグは同じ情報源から生成されるので、矛盾しません。保護機能の中ではデフォルトで有効です(保護機能自体は明示的に有効化する方式で、許可された環境では何もしません)。metaタグだけの動作にするには、ヘッダー送信を無効にします。
'indexing_guard' => [
'send_header' => env('SEO_INDEXING_GUARD_HEADER', true),
],ミドルウェアが登録されるのは保護機能を有効にした場合だけです。無効の場合、パッケージはミドルウェアスタックに何も追加しません。
静的ファイルはPHPを経由しません
Webサーバーがpublic/から直接返すファイルはLaravelを通らないため、このヘッダーを付与できません。Webサーバーの設定やCDNなどのエッジ側で保護してください。この機能は、アプリを経由するすべてのレスポンスを対象とします。
明示的なrobotsの値より優先される理由
Rankbeamの他の場所では、明示的に保存した値が優先されます。それが優先順位の仕組みの目的です。この保護機能は意図的に設けた唯一の例外で、明示的な値の層よりも上位に位置します。ここでのリスクは一方向だからです。
- ステージングのデータベースは通常、本番のコピーです。そのため、
index,followを保存したページは、その指示をステージングに持ち込み、インデックス登録を求めてしまいます。 - ステージングを誤ってインデックス登録すると大きな問題になりますが、誤って
noindexにしても、本来の目的には影響しません。 そのため、もともとインデックス登録を望まない環境では、保存した値でも覆せない最低限の安全策として保護機能を適用します。
有効にする
保護機能は無効の状態で提供されます。パッケージをインストールまたはアップグレードしても、利用者が明示的に有効にしない限り、本番以外の環境の出力は変わりません。リゾルバーのblank_is_unsetや生成OG画像と同じく、有効化するまでは出力がバイト単位で変わらない方針です。次の1行で有効にできます。
SEO_INDEXING_GUARD=trueデフォルトの許可リストではproductionに影響しないため、共有設定で保護機能を有効にしたままにできます。インデックス登録したい環境がすべて許可リストに含まれていることを確認してください。利用を強く推奨しており、Core 4ではデフォルトで有効にする候補となっています。
無効にする場合も1行です。
SEO_INDEXING_GUARD=falseインデックス登録を許可する環境を選ぶ
デフォルトではproductionだけを許可します。カンマ区切りの環境変数でリストを変更できます。
# Let a public preview environment index too
SEO_INDEXING_GUARD_ALLOWED="production,prod-eu"または、config/seo.phpで設定します。
'indexing_guard' => [
'enabled' => env('SEO_INDEXING_GUARD', false),
'allowed_environments' => ['production', 'prod-eu'],
],各項目はStr::is()で照合されるため、ワイルドカードが使えます。たとえば'prod*'はproductionとprod-euに一致します。
'allowed_environments' => ['prod*'],空のリストは、インデックス登録を許可する環境がないことを意味します。保護機能はすべての環境で有効になり、安全側に倒れます。ただし、環境変数SEO_INDEXING_GUARD_ALLOWEDの値が空または空白の場合は['production']にフォールバックします。入力ミスで本番が知らないうちにインデックス登録対象から外れないようにするためです。本当に「すべての環境」を保護したい場合は、設定ファイルに[]を明示してください。
動作を確認する
seo:auditはバナーを表示し、--jsonでは機械可読の状態も返します。
php artisan seo:audit --json{
"indexing_guard": {
"active": true,
"environment": "staging",
"allowed_environments": ["production"],
"directive": "noindex,nofollow"
},
"pages": [ /* ... */ ]
}保護対象の環境で配信または生成されるrobots.txtは、次のようになります。
# robots.txt — managed by Rankbeam
# Indexing guard ACTIVE: this app is running in the "staging" environment,
# which is not in seo.indexing_guard.allowed_environments. Every crawler is
# disallowed so this non-production site stays out of search results.
# https://rankbeam.dev/guide/indexing-guard
User-agent: *
Disallow: /対象範囲
この機能が制御するのは、robots metaタグ、X-Robots-Tagヘッダー、robots.txtというインデックス登録に関する指示です。タイトル、ディスクリプション、正規URL、スキーマには触れません。また、robotsの出力方針(seo.robots.emit_default)とは独立しています。noindex,nofollowはサイトのデフォルトと異なるため、常にタグとして出力されます。