HTTP-коды и перенаправления
Внешний вид страницы не определяет ответ сервера. Посетитель может увидеть аккуратное сообщение «Страница не найдена», тогда как сервер сообщит роботу об успешной загрузке. Бывает и обратная проблема: нужный старый адрес в итоге открывает урок, но перед этим проходит несколько перенаправлений. В обоих случаях визуального просмотра недостаточно.
В этом уроке рассмотрим исправный старый урок B, удалённый материал F и прежний адрес H. Для example.com используются условные ответы нашего стенда. Показанные ниже команды и фрагменты — примеры для выполнения на собственном сайте, а не результаты запроса к ProfessorWeb. У зарезервированного домена example.com нет нашего учебного проекта, поэтому подстановка этого имени в команду не воспроизведёт заданные ответы.
Статус, заголовки и тело ответа
При загрузке HTML сервер передаёт несколько частей ответа. Код статуса сообщает результат обращения, заголовки содержат дополнительные сведения, а тело — сам документ. У обычной статьи это может быть статус 200, тип text/html и текст урока. У перенаправления существенны код из диапазона 3xx и заголовок Location, который задаёт следующий адрес.
Зафиксируем исходные условия:
| ID | Адрес на https://example.com |
Учебное поведение до исправления |
|---|---|---|
| B | /my/LINQ/linq_xml/level7/7_1.php |
200, HTML старого урока |
| F | /articles/removed-example.html |
200, HTML с сообщением «Страница не найдена» |
| H | /old/xml-queries.html |
301 на /temporary/xml-queries.html, затем 302 на B, затем 200 |
Для проверки используем curl, который позволяет сохранить заголовки и документ раздельно. Если вы работаете в Windows PowerShell, при необходимости указывайте curl.exe, чтобы вызвать именно программу curl. Папка seo-audit из предыдущих уроков уже должна существовать; команды ниже выполняются из её родительской папки. В примерах показаны имена файлов, которые затем можно вписать в evidence.
Сначала загрузим B обычным GET-запросом без автоматического перехода по редиректам:
curl --silent --show-error \
--dump-header seo-audit/b-get-headers.txt \
--output seo-audit/b-get.html \
'https://example.com/my/LINQ/linq_xml/level7/7_1.php'
Здесь --dump-header сохраняет заголовки, а --output — тело. --silent убирает индикатор прогресса, --show-error оставляет сообщения о неудаче сетевого обращения. Параметр --location пока не указан: нас интересует первый ответ точного проверяемого адреса. Значение параметров описано в руководстве curl.
В учебном сценарии первая строка сохранённых заголовков означает 200; конкретная запись может зависеть от версии протокола, например HTTP/2 200. В HTML должен находиться именно урок B. Если в файле оказался экран авторизации, заглушка защиты или сообщение об ошибке, один успешный статус не подтверждает получение нужного материала. Запишите оба наблюдения, а не только число.
Почему HEAD не заменяет GET
Для быстрой проверки заголовков часто используют команду с --head:
curl --silent --show-error --head \
'https://example.com/my/LINQ/linq_xml/level7/7_1.php'
Она отправляет HEAD-запрос. По HTTP его ответ не содержит тело документа; сервер должен сообщать сведения, соответствующие GET, с разрешёнными стандартом особенностями формирования заголовков. Назначение метода описано в RFC 9110, раздел 9.3.2.
На практике настройки сервера, промежуточного кеша или приложения могут обрабатывать методы неодинаково. Поэтому результат HEAD — отдельное наблюдение. Если он сообщает 200, а GET возвращает 404, нельзя выбрать удобное число и закончить диагностику. Нужно исследовать расхождение. Тем более HEAD не покажет, что внутри ответа с 200 находится пустая страница.
Для нашего аудита основная запись о содержимом опирается на GET. В evidence полезно указать метод запроса, точный URL, файлы ответа и момент обращения. Обычный curl с вашим компьютером тоже не доказывает, что настоящий поисковый робот получил тот же документ: правила защиты и условия запроса могут различаться. При расхождении сравните его с проверкой поисковой системы, сохранив оба источника.
При сетевой ошибке — например, невозможности установить соединение — кода HTTP может вообще не быть. Такое значение следует записать как unknown, а ошибку сохранить отдельно. Оно не превращается в 404: отсутствие ответа сервера и ответ «ресурс не найден» относятся к разным ситуациям.
Удалённая страница F
Теперь аналогично загрузим F:
curl --silent --show-error \
--dump-header seo-audit/f-get-headers.txt \
--output seo-audit/f-get.html \
'https://example.com/articles/removed-example.html'
Учебный ответ содержит 200, но в HTML написано «Страница не найдена». Это противоречие между объявленным результатом запроса и содержанием. Такой документ является кандидатом на soft 404, то есть ситуацию, когда поисковая система распознаёт отсутствие полезной страницы при формально успешном ответе. Самостоятельного чтения сообщения недостаточно, чтобы утверждать наличие уже зарегистрированной ошибки Google: классификация подтверждается соответствующим поисковым отчётом. HTTP-статусы и soft 404 в Google.
По нашему сценарию материал F действительно удалён и замены ему нет. Поэтому ожидаемое исправление — ответ 404 по этому адресу. При этом тело ответа может оставаться полноценной страницей с навигацией, поиском и объяснением. Полезное оформление страницы ошибки не требует успешного кода.
В статическом проекте для этого недостаточно создать красивый файл 404.html. Хостинг должен использовать его для отсутствующих маршрутов с правильным статусом, а не отдавать главную со статусом 200 для любого пути. Где задаётся такое поведение, зависит от веб-сервера и платформы. В журнале изменений укажите конкретный источник настройки, который отвечает за F, и проверьте, что исправные страницы вроде B продолжают возвращать свой материал.
Ожидаемый фрагмент ответа F после изменения выглядит так:
HTTP/1.1 404 Not Found
Content-Type: text/html; charset=utf-8
Это иллюстрация результата, а не полный набор реальных заголовков. Для Google 4xx означает отсутствие пригодного содержимого по этому адресу; ранее использовавшийся URL со временем перестаёт использоваться. Если страница удалена без замены, 404 сам по себе не является неисправностью сайта. Обработка HTTP-ошибок роботами Google.
Не перенаправляйте все удалённые документы на главную только для исчезновения ошибки. Пользователь, пришедший за определённым материалом, попадёт в другое место, а смысл отсутствия ресурса потеряется. Перенаправление подходит, когда существует соответствующая замена. В нашем примере такая замена есть у H, но не у F.
Как увидеть всю цепочку H
У H первый ответ — постоянный редирект на временный промежуточный путь. Чтобы исследовать его, сначала выполните запрос без --location. В учебном стенде существенный фрагмент будет таким:
HTTP/1.1 301 Moved Permanently
Location: https://example.com/temporary/xml-queries.html
Затем отдельной загрузкой промежуточного URL можно обнаружить 302 на B. Такой разбор полезен, когда браузер незаметно показывает только конечный документ. Конечный 200 ничего не говорит о количестве предшествовавших переходов.
Для сохранения всех заголовков и конечного тела воспользуемся --location. Ограничение --max-redirs останавливает слишком длинную цепочку в рамках этой команды; значение 5 здесь является нашим диагностическим ограничением, а не правилом поисковых систем:
curl --silent --show-error --location --max-redirs 5 \
--dump-header seo-audit/h-chain-headers.txt \
--output seo-audit/h-final.html \
--write-out 'status=%{http_code}\nurl=%{url_effective}\nredirects=%{num_redirects}\n' \
'https://example.com/old/xml-queries.html'
В начальном учебном состоянии ожидается следующий заключительный вывод:
status=200
url=https://example.com/my/LINQ/linq_xml/level7/7_1.php
redirects=2
Число 2 означает два перехода, а status=200 относится к конечному ответу. Первый статус нужно читать в файле заголовков. Там будут последовательно представлены ответы H, промежуточного адреса и B. Назначение --location и переменных --write-out описано в официальной документации curl.
В обычном HTTP 301 означает постоянное перемещение, 302 — временное. Если старый материал окончательно переехал на соответствующий адрес, постоянное перенаправление выражает это решение. Google также различает постоянные и временные редиректы при выборе канонического URL. Переадресация и Google Поиск.
Мы рассматриваем GET-загрузку учебных страниц. Приложения с формами и другими методами требуют отдельного внимания к семантике перенаправления; копировать правило для HTML-урока на обработчик оплаты нельзя. Для H задача проста: удалить промежуточную точку и настроить один 301 прямо на B, сохранив сам старый адрес рабочим для существующих ссылок.
Повторная загрузка после изменения
После исправления ожидаемая цепочка имеет вид H → 301 → B → 200. Для той же команды curl число перенаправлений станет 1, конечный адрес останется B, а первая строка файла заголовков по-прежнему должна сообщать постоянное перемещение. Файл h-final.html должен содержать урок, а не только иметь подходящее имя.
В observations-template.csv записывайте новые фактические ответы отдельными строками типа http. В changes-template.csv оформите два изменения: F получает корректный ответ отсутствия, H получает прямой переход. Их ожидаемые результаты различаются, поэтому объединять их формулировкой «починили все ошибки» неудобно для дальнейшего сравнения.
Пока F и H ещё остаются ошибочными записями старого Sitemap: очистку карты мы выполним в отдельном уроке. Это сознательное разделение задач. Сейчас получаем согласованное сетевое поведение, затем согласуем список предпочитаемых документов и внутренние ссылки. Сам адрес B и его содержимое менять не требуется.
Для самостоятельного разбора представьте, что после настройки H браузер открывает B, а команда без автоматических переходов показывает 302. Это означает, что нужный документ достижим, но объявленная постоянность перехода ещё не соответствует замыслу. Правильный вывод опирается одновременно на первый ответ, всю цепочку и конечный материал — именно эти наблюдения понадобятся нам при проверке дублей.