Перейти к содержимому

Защита от индексации (страховка для окружений вне production)

Попадание staging или локальной копии сайта в Google — одна из самых распространённых и неприятных SEO-ошибок: дубликаты конкурируют с настоящими страницами, закрытое окружение оказывается в индексе, а очистка инструментом удаления URL занимает недели. Типичная причина — noindex только в .env, который забыли задать, или правило robots, перезаписанное при развёртывании.

Защита от индексации затрудняет такую ошибку на уровне устройства приложения. Привяжите возможность индексации к окружению Laravel вместо флага, о котором нужно помнить: если приложение работает вне списка разрешённых окружений, для каждой страницы принудительно задаётся noindex,nofollow, управляемый robots.txt запрещает обход всем роботам, а seo:audit явно сообщает об этом.

Это бесплатная функция ядра.

Что происходит при активной защите

Если app()->environment() не входит в seo.indexing_guard.allowed_environments и защита включена, автоматически выполняются четыре действия:

  1. Резолвер принудительно задаёт noindex,nofollow каждой странице. Это применяется выше всей цепочки приоритетов и переопределяет даже явное значение robots страницы, сохранённое в seo_meta.
  2. HTTP-заголовок X-Robots-Tag: noindex,nofollow отправляется с каждым ответом, проходящим через приложение. См. ответы не в HTML ниже.
  3. SEO::robotsTxt()->build() создаёт robots.txt с полным запретом обходаai.txt): обычные User-agent: * / Disallow: /. Это охватывает команду seo:robots-txt и необязательный динамический маршрут.
  4. seo:audit выводит заметное сообщение, чтобы состояние «всё с noindex» не стало неожиданностью при чтении отчёта.

В разрешённых окружениях (по умолчанию production) защита полностью бездействует: вывод не меняется и побайтно идентичен.

Ответы не в HTML (PDF, ленты, изображения)

Принудительный метатег robots доступен только роботам, разбирающим HTML: у PDF, RSS/Atom-ленты, изображения и любого другого ответа не в HTML нет <head>. Поэтому активная защита также отправляет ту же директиву HTTP-заголовком через глобальный middleware:

http
X-Robots-Tag: noindex,nofollow

Заголовок и метатег берутся из одного источника и не могут расходиться. Заголовок включён по умолчанию внутри защиты (сама защита включается по желанию и бездействует в разрешённых окружениях). Отключите его, чтобы оставить только метатег:

php
'indexing_guard' => [
    'send_header' => env('SEO_INDEXING_GUARD_HEADER', true),
],

Middleware регистрируется только при включённой защите, поэтому выключенная функция ничего не добавляет в стек.

Статические файлы обходят PHP

Файл, который веб-сервер напрямую отдаёт из public/, не попадает в Laravel и не может получить этот заголовок. Защитите такие файлы на внешнем уровне (конфигурация веб-сервера / CDN). Функция охватывает всё, что проходит через приложение.

Почему переопределяется явное значение robots

Во всех остальных случаях Rankbeam отдаёт приоритет явно сохранённому значению — в этом смысл цепочки приоритетов. Защита — намеренное исключение выше явного слоя, поскольку риск здесь односторонний:

  • База staging обычно скопирована из рабочего окружения, поэтому страница с сохранённым index,follow принесла бы эту директиву в staging и попросила индексацию.
  • Ошибочно индексировать staging опасно; ошибочно назначить ему noindex ничего не меняет. Поэтому защита задаёт границу, которую сохранённое значение не может обойти, именно в тех окружениях, где индексация никогда не нужна.

Включение

Защита поставляется выключенной, поэтому установка или обновление пакета не меняет вывод окружения вне production без вашего решения. Это та же политика побайтно неизменного вывода до явного включения, что у blank_is_unset резолвера и создаваемых OG-изображений. Включите одной строкой:

dotenv
SEO_INDEXING_GUARD=true

Со стандартным списком разрешённых production не затрагивается, поэтому защиту можно оставлять включённой в общей конфигурации. Проверьте, что список включает все окружения, которые вы хотите индексировать. Защита настоятельно рекомендуется и рассматривается для включения по умолчанию в Core 4.

Отключите такой же одной строкой:

dotenv
SEO_INDEXING_GUARD=false

Какие окружения могут индексироваться

По умолчанию разрешено только production. Переопределите список переменной окружения со значениями через запятую:

dotenv
# Let a public preview environment index too
SEO_INDEXING_GUARD_ALLOWED="production,prod-eu"

Или в config/seo.php:

php
'indexing_guard' => [
    'enabled' => env('SEO_INDEXING_GUARD', false),
    'allowed_environments' => ['production', 'prod-eu'],
],

Записи сопоставляются через Str::is(), поэтому работают шаблоны с подстановочными знаками: 'prod*' соответствует production и prod-eu:

php
'allowed_environments' => ['prod*'],

Пустой список означает, что ни одно окружение не может индексироваться: защита активна везде (безопасное направление отказа). При этом пустое или состоящее из пробелов значение переменной SEO_INDEXING_GUARD_ALLOWED использует ['production'] как резервное, чтобы опечатка не могла незаметно запретить индексацию рабочего сайта. Если действительно нужно «везде», явно задайте [] в конфигурации.

Проверка

seo:audit показывает сообщение, а в --json передаёт машиночитаемое состояние:

bash
php artisan seo:audit --json
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, заголовком X-Robots-Tag и robots.txt. Она не меняет заголовки страниц, описания, канонические URL или schema.org и независима от политики вывода robots (seo.robots.emit_default): поскольку noindex,nofollow отличается от значения сайта по умолчанию, тег выводится всегда.

rankbeam/laravel-seo распространяется под лицензией MIT.