Cloudflare Browser Run: параллельный SEO-аудит после деплоя
Cloudflare Browser Run: параллельный SEO-аудит после деплоя
Рецепт подготовлен 30 сентября 2026 года. Он собирает технические признаки нескольких заранее согласованных страниц после релиза, а не ставит сайту универсальную «оценку SEO».
Что изменилось 29 сентября
Cloudflare разрешила несколько одновременных подключений к одной Browser Run session. Каждому puppeteer.connect() соответствует отдельное CDP-соединение. Это полезно для независимых исполнителей, использующих один браузер. Параллельные вкладки сами по себе не являются новой возможностью этого релиза.
Для concurrent connections достаточно @cloudflare/puppeteer >= 1.1.0. В нашем примере используется 1.4.0, поскольку hostname guardrails требуют 1.4.0 или новее. Это две разные границы совместимости.
Пример создаёт собственную сессию на один небольшой пакет заданий: два независимых CDP-клиента одновременно, новый browser context для каждого URL. Он демонстрирует новый способ подключения внутри одного Worker, не реализует распределённый пул между несколькими Workers.
Что именно проверяется
| Наблюдение | Что сохраняет пример | Что нельзя заключить автоматически |
|---|---|---|
| Документ | HTTP-статус, Content-Type, итоговый URL | HTTP 200 не доказывает, что вместо страницы не показана заглушка |
| Метаданные | Rendered title, число canonical и их цели | Найденный canonical не является выбранным Google canonical |
| Директивы | X-Robots-Tag, meta robots и googlebot | Не вычисляется полный effective policy с учётом всех user-agent директив |
| DOM | Количество H1 и ссылок | Не проверяется качество текста и работоспособность каждой ссылки |
| Runtime | Счётчики page errors и failed requests | Неудачный запрос аналитики не обязательно ломает контент |
| Сбор данных | Этап отказа, cleanup, закрытие браузера | collected означает «данные собраны», не «страница прошла SEO-аудит» |
Для проверки исходного HTML, полной redirect chain, robots.txt, sitemap, индексации и ссылочных ответов нужны отдельные проверки. Этот минимальный пример их не реализует. Не выдавайте запуск обычного браузера за точную эмуляцию Googlebot.
Исходники и устройство примера
Код находится в scripts/browser-run-seo-audit:
audit.mjs— проверка конфигурации, ограниченная параллельность, сбор признаков и cleanup;index.mjs— Worker сscheduled-обработчиком и сохранением JSON в приватный R2;audit.test.mjs— offline-тесты на подставном CDP-клиенте;wrangler.example.json— конфигурация без расписания и с выключенным аудитом.
1–10 согласованных путей одного сайта
→ владелец пакета запускает один браузер с hostname allowlist
→ два потребителя независимо подключаются по CDP
→ каждый URL получает новый context
→ ответ документа + ожидание готовности + DOM
→ context.close() → client.disconnect()
→ все потребители завершены → владелец закрывает браузер
→ JSON в приватном REPORTS bucket
Отдельные contexts разделяют cookies и storage. По документации повторного использования сессий обычный клиент должен отключиться через disconnect(), а не останавливать общий браузер. В примере close() вызывает только владелец пакета, когда все потребители завершились. Даже ошибка context.close() не отменяет попытку disconnect().
Contexts не являются отдельными браузерными процессами или защитой от недоверенного клиента с доступом к browser-wide CDP. Не смешивайте в одной сессии разные доверительные контуры. Для нескольких Workers нужны координатор, квоты и атомарное получение слота; счётчик contexts, прочитанный перед запуском, сам по себе не устраняет гонку.
Запуск: сначала offline, затем отдельное тестовое окружение
Из рабочей копии репозитория:
cd scripts/browser-run-seo-audit
node --test audit.test.mjs
Для этих тестов не нужны npm-зависимости, Cloudflare credentials или доступ к сайтам. Они проверяют логику кода на fixtures, не настоящий браузер.
Для интеграционной проверки скопируйте каталог в отдельный Worker-проект. Установочные команды меняют только этот проект, не зависимости SEO Recipes:
npm install
npm install --save-dev --save-exact wrangler@4
cp wrangler.example.json wrangler.json
Зафиксируйте полученные package.json и lockfile перед CI. В поставляемом примере lockfile не создан: установка SDK и Wrangler при подготовке материала не выполнялась. Версия Puppeteer в package.json закреплена как 1.4.0; версию Wrangler выберите, проверьте и закрепите в своём окружении.
В wrangler.json уже указаны nodejs_compat и compatibility_date: "2026-09-30". Настройте собственный непубличный R2 bucket для REPORTS; не включайте для него публичный домен или r2.dev. Задайте срок хранения отчётов. R2 и Browser Run должны быть доступны в выбранном аккаунте; создание ресурсов и деплой здесь не выполняются.
AUDIT_CONFIG — JSON-строка в переменной Worker. После разбора она имеет такую форму:
{
"origin": "https://example.com",
"paths": ["/", "/docs/"],
"assetHosts": [],
"readySelector": "main"
}
Замените примерный домен и пути на собственный разрешённый сайт. Список можно подготовить из своего sitemap, но извлечение sitemap в код не включено. Ограничение — 10 входных путей и два потребителя. Query, fragment, credentials, IP-literals и переход на другой origin в конфигурации не принимаются. IDN-домены, в том числе .рф, поддерживаются: origin и asset-hostnames нормализуются через WHATWG URL в ASCII/Punycode до проверки DNS-меток и удаления дублей. Проверяются длины имени и меток; локальные имена, wildcard и URL вместо asset-hostname отклоняются. Это не универсальная защита от SSRF: конфигурация должна оставаться доверенной, а DNS и содержимое разрешённых доменов — под контролем оператора.
readySelector должен обозначать готовность важных данных приложения. main в конфигурации — пример, а не гарантия завершения hydration. Для SPA лучше отдельный маркер, выставляемый после обновления метаданных. Если маркер не появился, результат будет failed на этапе ready-selector, а не ложным зелёным отчётом. Навигация ограничена 15 секундами, ожидание selector — 5 секундами; это не жёсткий общий deadline всех CDP-операций.
Аудит изначально выключен (AUDIT_ENABLED: "false"), HTTP-обработчик всегда отвечает 404, Cron Triggers отсутствуют. Включайте AUDIT_ENABLED: "true" только для отдельного теста после настройки доменов и хранилища. Для локального вызова scheduled handler используется тестирование Cron Triggers:
npx wrangler dev
# В другом терминале, адрес локального dev-сервера:
curl --fail 'http://localhost:8787/cdn-cgi/local/scheduled?format=json'
Для проверки именно удалённой возможности нескольких соединений настройте browser binding с "remote": true по руководству reuse sessions. Такой запуск обращается к реальному Browser Run и расходует квоту; R2 при локальном запуске имеет свои отдельные настройки local/remote. Не считайте локальную эмуляцию подтверждением cloud-интеграции. В рамках статьи scheduled handler, R2 и удалённый браузер не запускались.
После теста выключите аудит. Production-расписание, защита от перекрывающихся пакетов и автоматическая отправка отчёта в PR остаются отдельной интеграцией. Локальный лимит два не ограничивает суммарный параллелизм нескольких одновременно запущенных пакетов.
Сетевые ограничения и данные отчёта
Guardrails задаются при запуске сессии и ограничивают HTTP/HTTPS по hostname, не по пути или HTTP-методу. Пример всегда передаёт явный непустой allowlist: origin и выбранные точные asset-hostnames. Wildcards и автоматически расширяемые наборы CDN не используются. При заблокированном ресурсе проверьте необходимость домена, а не снимайте защиту целиком.
Hostname guardrails не заменяют авторизацию и контроль иных протоколов. Даже без кликов JavaScript страницы может отправлять запросы и менять состояние сервиса; выбирайте публичные безопасные страницы либо disposable staging. Не передавайте этому примеру сессии администратора, SQL/Cloud API credentials или приватные пользовательские данные.
Отчёт не содержит полные HTML, скриншоты, cookies и тела console messages. Из URL удаляются query и fragment, наличие отмечается флагами. Если canonical использует query-параметры, проверяйте их отдельно в защищённом окружении: редактирование отчёта преднамеренно не сохраняет такие значения. Canonical разбирается целиком до сокращения: даже после длинного пути сохраняются корректные hasQuery и hasFragment, но сами параметры в отчёт не попадают. Если очищенный URL длиннее 1024 символов, возвращается truncated: true; отсутствие этого поля означает, что URL не сокращался. Сокращённую цель нельзя считать полным адресом при сравнении canonical. Пустой href canonical не подменяется self-canonical. Title и пути всё равно могут содержать чувствительные данные — отчёты должны оставаться приватными.
Запуск не обходит CAPTCHA и WAF. Учитывайте правила сайта, robots.txt и особенности методов Browser Run; custom Puppeteer-навигация не реализует собственный robots-parser. При отказе доступа выясните причину и согласуйте проверку, не отключайте защиту рабочего сайта для всех посетителей.
Как читать и сравнивать JSON
Сначала проверьте browserClosed, затем state и cleanup каждой страницы. failed и skipped нельзя превращать в нулевые значения метрик. При HTTP-ошибке или неожиданном origin данные DOM не объявляются собранными. Ошибка очистки также делает результат неполным.
После goto() код сверяет основной запрос и ответ, изменения main frame и URL документа до и после чтения DOM. JavaScript-редирект, перезагрузка по тому же URL или смена маршрута во время сбора дают state: "failed", stage: "document-changed" без смешанных HTTP-заголовков и DOM. Даже незавершённый новый запрос делает образец неполным. HTTP-редиректы, завершённые самим goto(), допустимы, если его итоговый ответ соответствует читаемому документу. Навигация iframe и загрузка ресурсов не считаются сменой главного документа. Это консервативная проверка согласованности, не автоматическое следование цепочке клиентских переходов; новую страницу проверяйте отдельным согласованным запуском.
Для регрессии сравнивайте одинаковый набор URL, готовность приложения и условия запуска до/после релиза. Отдельно рассматривайте исчезновение title, изменение canonical, неожиданные директивы индексации и ошибки основного приложения. collected с HTTP 200 может оказаться страницей входа или challenge: нужны контрольный selector и ручная проверка. Сохраняйте commit SHA деплоя во внешнем журнале, не делайте причинный вывод по одному совпадению времени.
Для оценки экономии измерьте исходный вариант и shared-session вариант на том же наборе страниц: число launches, полное время, долю неполных результатов и оплачиваемое использование. Не обещайте пропорциональное ускорение или снижение счёта: страницы конкурируют за ресурсы браузера. Лимиты и тарификацию проверяйте для своего плана, с учётом Workers, хранения и очередей, если они используются.
Альтернатива для большого сайта: managed crawl и Queues
Это отдельный вариант архитектуры, не функция приведённого Worker. Managed /crawl сам обнаруживает страницы и возвращает результаты задания. С 25 сентября для crawl jobs доступны события crawl.started, crawl.updated, crawl.finished в Cloudflare Queues; источник — журнал Browser Run и схемы событий.
managed /crawl → crawl.finished → Queue consumer
→ проверить свой job/account и итоговый статус
→ получить результаты задания
→ сопоставить с baseline → приватный отчёт
Подписка имеет account-level охват: фильтруйте задания проекта, а не обрабатывайте всё как один аудит. Consumer должен выдерживать повторы, сохранять прогресс и не считать событие finished доказательством успешной обработки каждой страницы. Пагинация результатов, retries, DLQ и бюджет требуют отдельной реализации. Событие lifecycle не следует считать полным HTML-отчётом.
Обычный page.goto() в нашем примере не создаёт managed crawl job и не публикует эти события автоматически. Для custom-аудита публикацию собственных сообщений в очередь нужно реализовать явно.
Проверки примера и границы готовности
Offline-тесты проверяют конфигурацию до расходования квоты, URL-redaction, лимит параллельности, отдельный context на URL, порядок cleanup, ошибки connect/context/navigation/readiness, отказ закрытия, HTTP/non-HTML/redirect и пустой canonical. Дополнение 1 октября 2026 года: 30 offline-тестов проходят, включая IDN, длинный canonical, клиентские переходы и same-URL reload до завершения goto(), во время ожидания и чтения DOM. Тестовая среда исполняет настоящий callback извлечения на минимальном DOM в отдельном контексте Node.js, а не возвращает заранее подготовленные метаданные. Используется подставной CDP-клиент: это не проверка сетевой изоляции настоящего Chromium и не тест rendering.
Перед внедрением отдельно проверьте установку зависимостей и bundle, remote binding, отказ запрещённого hostname, недоступность ready selector, запись в приватное хранилище, лимиты и закрытие сессии в dashboard. Не подключайте автоматический release gate до этой интеграционной проверки.
Связанные материалы
Документация Cloudflare использована как источник возможностей API и версий. Схема ограниченного аудита, код и checklist — пример SEO Recipes, не гарантия поддержки или SLA Cloudflare.
