SEO и контент

301, 302, 404, 410 и soft 404: какие HTTP-коды действительно важны для SEO

Страница удалена.

Пользователь открывает старую ссылку и видит:

Страница не найдена.

Кажется, всё настроено правильно.

Но сервер отвечает:

HTTP/1.1 200 OK

Для человека это экран ошибки.

Для поискового робота — успешно существующая страница.

Или другая ситуация.

Старая статья переехала:

/expert/old-url

на:

/expert/new-url

Разработчик делает redirect.

Но использует:

302 Found

хотя перенос постоянный.

В браузере пользователь всё равно попадает туда, куда нужно.

Но поисковой системе отправляется другой смысл:

Перенос временный.

Именно поэтому HTTP-коды — это не техническая формальность, скрытая где-то между Nginx и браузером.

Для поискового робота они отвечают на фундаментальные вопросы:

Страница существует?
Страница переместилась?
Перемещение постоянное?
Перемещение временное?
Страница удалена?
Страница удалена навсегда?
Сервер временно сломан?

И если HTML говорит одно, а HTTP — другое, поисковая система вынуждена самостоятельно разбираться в противоречии.

Разберём пять состояний, которые особенно часто влияют на техническое SEO:

301
302
404
410
soft 404

И заодно посмотрим, где рядом с ними находятся 200, 503 и 429.


HTTP-код — это ответ сервера о состоянии ресурса

Когда поисковый робот запрашивает:

GET /expert/article

он получает не только HTML.

Сначала приходит примерно такой ответ:

HTTP/1.1 200 OK
Content-Type: text/html

И только после этого:

<html>
...
</html>

Статус:

200

говорит:

Запрос обработан успешно, ресурс существует.

Статус:

404

говорит:

Такого ресурса здесь нет.

301:

Ресурс окончательно переехал.

302:

Сейчас ресурс временно находится в другом месте.

410:

Этот ресурс был здесь, но удалён окончательно.

Поэтому SEO начинается ещё до <title>, H1 и canonical.

Если сервер неправильно описывает состояние URL, поисковой системе приходится сначала решить именно эту проблему.


Короткая таблица

Состояние URLПравильный ответ
Страница существует200
Страница навсегда переехала301
Перенос действительно временный302
URL не существует404
Ресурс сознательно удалён навсегда410
Сервер временно недоступенобычно 503
Слишком много запросов429
Страница визуально отсутствует, но сервер отдаёт 200soft 404 — ошибка архитектуры

Но главное — не запомнить таблицу, а понимать смысл каждого решения.


200 OK: страница действительно существует

Начнём с нормального состояния.

Публичная индексируемая статья:

https://example.ru/expert/api-versioning

существует и должна участвовать в поиске.

Ожидаем:

HTTP/1.1 200 OK
Content-Type: text/html

Внутри:

TITLE;
H1;
контент;
canonical;
structured data.

Для Яндекса это тоже базовое правило: необходимые страницы сайта должны возвращать 200 OK, а отсутствующие — корректный код ошибки.


Но 200 — не универсальный ответ на всё

Иногда frontend-разработчику проще настроить сервер:

любой URL
↓
index.html
↓
200 OK

Для SPA это действительно удобно.

Например:

/login
/account
/projects

обрабатываются клиентским router.

Но вместе с ними сервер начинает отдавать:

/this-page-does-not-exist-at-all
↓
index.html
↓
200

JavaScript потом рисует:

Страница не найдена.

Так возникает soft 404.


Что такое soft 404

Soft 404 — это ситуация, когда страница по смыслу является ошибкой или практически пустым документом, но сервер отвечает:

200 OK

Google прямо определяет soft 404 как ситуацию, когда URL показывает пользователю, что страницы не существует, но возвращает успешный HTTP-статус 200. К soft 404 Google может также отнести пустые или почти пустые документы — например при проблемах базы данных или JavaScript. Такие страницы могут исключаться из поиска.

Яндекс описывает ту же распространённую ошибку: заглушка «страница не найдена» возвращает 200, поэтому робот считает несуществующий URL действующей страницей. Это создаёт лишние документы и замедляет индексирование полезных URL.


Вот классический soft 404

Пользователь открывает:

/expert/nonexistent-article

и видит:

404

Материал не найден.

Перейти на главную.

Но реальный HTTP:

HTTP/1.1 200 OK

Число:

404

внутри дизайна не имеет отношения к HTTP-коду.

Можно написать огромными буквами:

404

на всю страницу.

Если сервер отправил:

200

технически это успешный документ.


Правильный вариант

Можно сохранить красивый интерфейс:

Материал не найден.

Возможно, он был удалён.

Популярные статьи:
...

Но HTTP должен быть:

HTTP/1.1 404 Not Found

Google отдельно подчёркивает: custom 404 можно сделать удобным и визуально таким же, как остальной сайт, но сервер всё равно должен возвращать настоящий 404.


Soft 404 бывает не только на странице «Не найдено»

Это особенно важно.

Допустим существует:

/product/4812

Сервер отвечает:

200

Но запрос к API товара упал.

Frontend отрисовал:

Не удалось загрузить товар.

или вообще:

<div id="app"></div>

без основного содержимого.

Google может увидеть практически пустой документ и классифицировать его как soft 404. В официальной документации среди причин называются проблемы базы данных, отсутствующий JavaScript и незагруженные важные ресурсы.


Поэтому soft 404 особенно опасен для CSR/SPA

Например:

GET /product/4812
↓
200 index.html
↓
JavaScript
↓
GET /api/products/4812
↓
404
↓
frontend показывает «товар не найден»

На серверном уровне всё ещё:

200.

Лучшее решение — настоящий статус на сервере

Если архитектура позволяет:

/product/not-existing
↓
HTTP 404

это наиболее понятная модель.

Google отдельно рекомендует использовать meaningful HTTP status codes и указывает, что для SPA client-side routing soft 404 требует специальной обработки, например перехода на серверный URL, возвращающий настоящий 404, либо как запасного варианта noindex на error page.


Теперь разберём настоящий 404

Код:

404 Not Found

означает:

Сервер не нашёл запрашиваемый ресурс.

Например существовала опечатка:

/expert/postgesql

вместо:

/expert/postgresql

Никакой страницы /postgesql никогда не было.

Идеальный ответ:

404.

404 — это не SEO-наказание

Иногда владельцы сайтов боятся:

На сайте есть 404 — поисковик подумает, что сайт плохой.

Корректный 404 для реально отсутствующего URL — нормальная работа HTTP.

Проблема не в самом существовании 404.

Проблема, например:

внутреннее меню ведёт на 404;

или:

важная статья случайно стала 404;

или:

в sitemap тысячи удалённых URL.

Но если кто-то запросил:

/random-nonsense-xyz

такой URL и должен вернуть 404.

Google рекомендует возвращать 404 или 410, если контент удалён и подходящей замены нет.

Яндекс аналогично рекомендует настоящий 404 для несуществующих страниц.


Красивый 404 и настоящий 404 прекрасно совместимы

Например:

HTTP/1.1 404 Not Found
Content-Type: text/html

а HTML:

Такой страницы нет.

Возможно, ссылка устарела.

[Экспертный центр]
[Услуги]
[Главная]

Это хороший UX.

Статус нужен роботу и клиенту.

Интерфейс — человеку.


Когда использовать 404

Хорошие сценарии:

случайный несуществующий URL;
удалённая страница без прямой замены;
невалидный slug;
несуществующая сущность.

Например:

/expert/article-that-never-existed
→ 404

или:

/products/999999999
→ 404

если такого товара действительно нет и не предусмотрена другая продуктовая политика.


Но не используйте 404, если страница переехала

Представим была статья:

/expert/api-old

Мы просто изменили slug:

/expert/api-versioning

Старый URL уже не должен отдавать:

404.

У страницы есть ясная замена.

Поэтому нужен:

301.

301 Moved Permanently

301 означает:

Этот ресурс навсегда перемещён на другой URL.

Например:

/expert/old-api-guide

переехал на:

/expert/api-versioning

Ответ:

HTTP/1.1 301 Moved Permanently
Location: https://example.ru/expert/api-versioning

Что происходит для пользователя

Он открывает:

/old-api-guide

и браузер автоматически отправляет его:

/api-versioning.

Что происходит для поисковой системы

Google рассматривает постоянный redirect как сильный сигнал, что конечный URL должен стать canonical. 301 и 308 относятся к постоянным redirects.

Яндекс также рекомендует 301 для постоянного изменения адреса страницы: старый документ перестаёт быть целевой страницей поиска, а робот обрабатывает новый URL.


Когда 301 — правильный выбор

Например:

сменился slug;
изменена структура раздела;
HTTP → HTTPS;
www → без www;
старый товар заменён прямым новым аналогом;
две статьи действительно объединены
в одну более полную.

Особенно важны семантически близкие замены

Было:

/expert/seo-canonical-old

Стало:

/expert/canonical-dubli-stranic-seo

Это один и тот же материал с новым URL.

301 естественен.


Но 301 не должен быть универсальной заменой 404

Представим удалено:

/expert/akciya-2019

Прямой современной замены нет.

Разработчик думает:

Чтобы не терять SEO, отправим всех на главную.

Получаем:

/akciya-2019
↓ 301
/

Но главная страница не является заменой материала про акцию 2019 года.


Redirect должен иметь смысл для пользователя

Хороший вопрос:

Пользователь, пришедший по старому URL, действительно ожидал увидеть то, что находится на новом?

Если:

да

301 оправдан.

Если:

нет

не нужно искусственно перенаправлять всё подряд.

В официальных рекомендациях Google логика именно такая: если страница перемещена или есть ясная замена — используйте 301; если контент удалён и аналогичной замены нет — 404 или 410.


Не делайте огромных redirect-цепочек

Плохо:

/article-v1
↓ 301
/article-v2
↓ 301
/article-v3
↓ 301
/article-v4

Если текущий URL:

/article-v4

лучше:

/article-v1 → /article-v4
/article-v2 → /article-v4
/article-v3 → /article-v4

Почему это лучше

Меньше:

сетевых переходов;
неопределённости;
задержки;
точек конфигурационной ошибки.

И понятнее архитектура URL.


Нужно обновлять и внутренние ссылки

Плохая схема:

Карточка статьи
↓
/old-url
↓ 301
/new-url

Redirect спасает старые внешние ссылки.

Но собственный сайт уже должен ссылаться:

сразу на /new-url.

Sitemap тоже должен содержать конечный URL

Не:

<loc>
https://example.ru/old-url
</loc>

если он возвращает 301.

А:

<loc>
https://example.ru/new-url
</loc>

Sitemap должен описывать актуальные страницы сайта, а не историю redirects.


Теперь 302 Found

302 означает временное перенаправление.

Концептуально:

Сейчас ресурс находится здесь, но исходный URL остаётся его постоянным адресом.

Пример

Есть:

/campaign

На несколько дней пользователей нужно отправлять:

/campaign-maintenance

после чего вернуть старую страницу.

В такой модели временный redirect имеет смысл.


Главное отличие от 301 — намерение

301:
переехали навсегда.
302:
временная ситуация,
исходный URL ещё понадобится.

Для Google это различие имеет SEO-смысл

В актуальной документации Google постоянные redirects используются как сигнал, что target должен стать canonical.

Временные redirects, включая 302 и 307, не используются таким же образом как сигнал постоянной смены canonical; исходный URL предполагается временно сохранённым как основной, хотя целевой URL может индексироваться при наличии других сигналов.


У Яндекса детали обработки отличаются

Яндекс описывает 302 как временное размещение ресурса по другому адресу и рекомендует использовать его именно для временных scenarios — например A/B-тестов или обслуживания. Для постоянного перемещения Яндекс рекомендует 301.

Именно поэтому практическое правило для обеих поисковых систем одинаковое:

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


Одна из самых частых SEO-ошибок

Разработчик пишет:

res.redirect('/new-url');

Framework по умолчанию отдаёт:

302.

А перенос на самом деле постоянный.

Приложение визуально работает идеально.

Через полгода всё ещё:

302.

Значит redirect status нужно указывать явно

Например:

301

для permanent migration.

Не полагаться на default framework behavior.


Когда 302 действительно нужен

Например:

временная промо-версия;
A/B-сценарий;
временный альтернативный маршрут;
страница ненадолго перенесена,
но исходный URL вернётся.

Когда 302 почти наверняка неправильный

Например:

старый slug заменён навсегда;
новая структура сайта;
сменился домен;
HTTP окончательно переводится на HTTPS.

Там нужен постоянный redirect.


Теперь 410 Gone

410 более конкретен, чем 404.

404 говорит:

Ресурс не найден.

410:

Ресурс сознательно удалён и больше не существует.

На сайте была временная страница:

/event-2024-registration

Мероприятие окончательно закончилось.

Страница больше никогда не должна возвращаться.

Прямой замены нет.

Можно использовать:

410 Gone

Яндекс прямо определяет 410 как permanently deleted resource

То есть ресурс окончательно удалён с сайта; если он не удалён, а переехал, рекомендуется 301.

Google для удалённого контента без замены разрешает и 404, и 410: оба сообщают, что страницы больше нет и она не должна индексироваться.


Что выбрать: 404 или 410?

Практически:

404

отлично подходит для:

неизвестного URL;
ошибки в адресе;
удалённого ресурса,
для которого не требуется
специально подчёркивать окончательное удаление.

410 логично использовать, когда приложение точно знает, что ресурс существовал и был сознательно удалён навсегда.

Например в БД есть tombstone:

article_id = 1842
status = permanently_deleted

Тогда:

410

семантически очень чист.


Но не нужно превращать выбор 404 vs 410 в SEO-мистику

Иногда встречается утверждение:

410 гарантированно удаляется из Google намного быстрее, поэтому всегда используйте его.

Официальная текущая документация Google рекомендует оба кода для удалённого контента без замены и не даёт оснований строить универсальную стратегию вокруг обещания конкретной разницы в скорости удаления.

Поэтому выбирать стоит прежде всего по смыслу системы.


Хорошая модель

URL никогда не существовал
→ 404
URL удалён,
но приложение не ведёт
отдельную семантику удаления
→ 404
ресурс сознательно
и окончательно удалён
→ 410
ресурс переехал
→ 301

Почему нельзя оставлять удалённую страницу с 200

Допустим статья удалена.

Вместо неё CMS отдаёт:

200 OK

и текст:

Материал был удалён.

Мы создали soft 404.

Яндекс прямо предупреждает, что такое поведение заставляет робота считать ошибочный URL действующей страницей и может замедлять индексирование полезных документов.

Google также может исключить такую страницу как soft 404.


Что делать с удалённым товаром

Это интереснее статей.

Представим товар:

/products/iphone-15-black

больше не продаётся.

Нужно понять почему.


Сценарий A: товар временно закончился

Через неделю он вернётся.

Не нужно:

404.

Страница всё ещё полезна.

Можно оставить:

200 OK

и показать:

Нет в наличии.

Сообщить о поступлении.

Похожие товары.

URL остаётся реальным документом.


Сценарий B: товар снят, но есть прямой новый аналог

Например старая модель полностью заменена:

/model-2025
→
/model-2026

Если для пользователя это действительно логичная замена:

301

может быть хорошим решением.


Сценарий C: товар снят навсегда, замены нет

Тогда:

404

или:

410.

В зависимости от модели сайта.


Не отправляйте все снятые товары на каталог

Плохо:

/old-phone
↓ 301
/smartphones

если категория не является реальной заменой конкретного товара.

Пользователь ожидал один товар, а оказался на огромном списке.


То же самое касается удалённых статей

Было:

/expert/old-article

Есть новая статья, полностью заменяющая её:

301 → /expert/new-article

Нет прямой замены:

404/410.

Не перенаправляйте всё на главную «для сохранения SEO»

Это очень распространённая идея:

любой старый URL
↓
301
↓
/

Так получается:

удалённая статья
→ главная

старый товар
→ главная

несуществующий slug
→ главная

опечатка
→ главная

Структурного смысла в таких redirects нет.


Что поисковику вообще делать с такой схемой?

Запрошен документ:

/how-to-configure-postgresql

А сервер утверждает:

Его постоянная замена — корпоративная главная страница.

Содержательно это неправда.

Поэтому redirects нужно проектировать по принципу реальной эквивалентности или понятного переноса, а не просто желания избежать 404.


Soft 404 может появиться и через redirect

Например любой отсутствующий URL:

/random-xyz

автоматически отправляется:

302 → /

или:

301 → /

Мы по-прежнему маскируем отсутствие ресурса, только теперь через redirect.

Для реально отсутствующего URL без эквивалентной замены правильнее честный 404/410.


Страница не найдена — это полезная информация

Иногда разработчики воспринимают 404 как неудачу сайта.

Но HTTP должен честно описывать состояние системы.

Страницы нет
→ 404.

Это лучше, чем притворяться:

Страница существует
→ 200,

или:

Она переехала на главную
→ 301.

404 в sitemap — уже другая проблема

Сам 404 нормален.

Но если sitemap.xml содержит:

1000 URL

и:

300 из них → 404,

сайт одновременно сообщает поисковику:

Вот мои важные страницы.

и:

30% из них не существуют.

Такой sitemap нужно исправлять.


В Sitemap должны находиться текущие indexable URLs

Обычно это:

200;
canonical;
актуальные.

После удаления страницы:

убираем URL из sitemap.

Если есть replacement:

добавляем новый URL.

Старые внешние ссылки при этом не нужно удалять вручную

Если старый URL имеет прямую замену:

301

продолжает принимать внешний traffic.

Именно для этого redirect и нужен.


Что происходит с внутренними ссылками

После удаления или миграции нужно искать:

<a href="/old-url">

по всему сайту.

Если redirect существует:

сайт технически не сломан.

Но внутренняя архитектура всё равно плохая:

internal link
↓
301
↓
final URL.

Собственные ссылки стоит обновить сразу на финальный адрес.


Например статья содержит:

Подробнее:
старый материал

но ссылка ведёт:

404.

Сам статус правильный.

Неправильна ссылка на него.

Яндекс Webmaster отдельно предоставляет диагностику broken links именно для таких ситуаций.


Проблема:

внутренняя ссылка ведёт
на удалённую страницу.

Решение:

исправить ссылку.

Не:

заставить удалённую страницу
возвращать 200.

Теперь важный сосед: 503 Service Unavailable

Хотя тема статьи про 301/302/404/410, без 503 легко допустить опасную ошибку.

Представим deployment.

База временно недоступна.

Главная страница вместо нормального содержимого возвращает:

404.

Это неправильно.

Страница не исчезла.

Сервис временно не способен её выдать.


Для временной серверной проблемы нужен другой смысл

Например:

503 Service Unavailable

говорит:

Ресурс сейчас временно недоступен.

Не:

Этого URL больше нет.

Почему это критично

Google отдельно предупреждает не использовать 404/403 для rate limiting или временных серверных проблем: 4xx-состояния означают проблему ресурса/запроса, а для перегрузки предназначены другие ответы, включая 429, 500 и 503 в соответствующих ситуациях.

Яндекс также не рекомендует маскировать server overload кодами отсутствующих документов и указывает, что 429 и 5xx сигнализируют роботу о проблемах доступности сервера.


То есть ошибка БД не должна превращать все статьи в 404

Плохой код:

const article = await db.find(slug);

if (!article) {
  return res.status(404).render('not-found');
}

Что если:

db.find()

не вернул:

null,

а произошёл timeout?

Это два разных состояния.


Нужно различать

Запрос к БД успешен,
article отсутствует
→ 404

и:

БД недоступна
→ 5xx/503

Иначе временный инфраструктурный сбой начинает сообщать поисковикам:

Все наши статьи удалены.

Это особенно важно после deployment

Представим:

12:00
релиз

12:01
migration ещё не закончилась

12:02
Googlebot приходит

Если сервер отдаёт:

404

на реальные страницы, мы передаём совершенно неправильный сигнал.


429 — тоже отдельное состояние

429 Too Many Requests

означает:

Клиент делает слишком много запросов.

Не:

страницы нет.

Поэтому нельзя бороться с crawler load так:

Googlebot слишком активен
→ отдаём ему 404.

Google отдельно предупреждает против такой практики.


Отдельная выдача статусов Googlebot тоже опасна

Например:

пользователь → 200
Googlebot → 410

чтобы «сэкономить crawl».

Google прямо не рекомендует подобное различие: оно создаёт риск cloaking-подобного поведения и сложных ошибок.

Серверная семантика URL должна быть одинаковой и понятной.


Как HTTP-коды связаны с canonical

Представим:

/old-url

имеет:

<link rel="canonical" href="/new-url">

но продолжает возвращать:

200.

Если old URL больше вообще не нужен, намного понятнее:

301 → /new-url.

Google считает redirect сильным canonicalization-сигналом, как и rel="canonical", но redirect одновременно убирает необходимость поддерживать старую страницу как отдельный документ.


Простая логика выбора

Если:

Два URL должны продолжать существовать, но описывают почти один документ.

Тогда:

canonical.

Если:

Старый URL больше не должен существовать как самостоятельная страница.

Тогда:

301.

Если:

Документа больше нет и замены нет.

Тогда:

404/410.

302 здесь не подходит

Если вы уверены:

старый URL больше никогда
не станет основным,

слово:

временно

уже неправильно описывает архитектуру.


Важный пример: переименование рубрики

Было:

/blog/article

Стало:

/expert/article

Навсегда.

Настраиваем:

/blog/article
↓ 301
/expert/article

Неправильно

302

«пока всё не устаканится».

Если архитектурное решение уже постоянное, поисковой системе стоит сообщать именно это.


Пример: временный эксперимент

Основная страница:

/prices

На неделю запускается альтернативная:

/prices-test

Потом всё вернётся обратно.

Здесь temporary redirect действительно может быть осмыслен:

302.

Но для A/B-теста URL иногда вообще можно не менять

Это уже зависит от архитектуры эксперимента.

Главное — не использовать постоянную семантику для временного процесса и наоборот.


Что делать с фильтрами и параметрами

Например:

/catalog?sort=price

не обязательно требует redirect.

URL может существовать и возвращать:

200.

Если это альтернативное представление той же категории, canonical может указывать на базовый URL.

HTTP-статусы и canonical решают разные задачи.


Не пытайтесь заменить canonical редиректом без необходимости

Если пользователь специально выбрал:

sort=price,

redirect на:

/catalog

может сломать функцию сортировки.

То есть:

200 + canonical

может быть правильнее.


А вот tracking-параметр можно иногда вообще очистить redirect’ом

Например:

/article?utm_source=...

Но обычно его сохраняют для analytics и используют canonical на чистый URL.

Главное — чтобы выбранная политика была последовательной.


Разберём несколько практических кейсов

Кейс 1. Изменили slug статьи

Было:

/expert/api-old

Стало:

/expert/api-versioning

Решение:

301.

Кейс 2. Статья удалена без замены

/expert/obsolete-article

Решение:

404

или:

410.

Кейс 3. Материал окончательно удалён по решению редакции

И система явно знает:

permanently_deleted = true.

Логичный вариант:

410.

Кейс 4. Страница временно переехала

Исходный URL скоро снова станет основным.

Решение:

302.

Кейс 5. Неизвестный slug в SPA

/expert/random-abc

UI показывает:

Страница не найдена.

Но HTTP:

200.

Это:

soft 404.

Нужно добиться настоящего 404 либо корректной альтернативной indexing policy для архитектуры SPA.


Кейс 6. База данных временно недоступна

Реальная статья есть.

Просто backend не может её загрузить.

Не:

404.

А:

5xx / 503

в зависимости от характера сбоя.


Кейс 7. Товар временно закончился

Страница остаётся полезной.

200.

Кейс 8. Товар навсегда заменён новой моделью

Есть очевидный successor.

301.

Какие ошибки особенно часто встречаются в Nginx

Например:

try_files $uri /index.html;

для SPA.

В результате:

/anything-at-all

получает:

index.html
+
200.

Для application routes это удобно.

Для несуществующих SEO routes — опасно.


Поэтому router и Nginx должны договариваться

Например публичные SSR routes:

/expert/*
/services/*
/portfolio/*

должны уметь возвращать настоящий status.

Не просто падать в универсальный SPA shell.


Ещё одна распространённая ошибка

Nginx:

error_page 404 /index.html;

а внутренняя конфигурация меняет ответ на:

200.

Визуально пользователь видит приложение.

Но HTTP-семантика потеряна.


Проверяйте не браузером, а HTTP-запросом

Например:

curl -I https://example.ru/does-not-exist

Ожидаем:

HTTP/2 404

Для redirect

curl -I https://example.ru/old-url

Ожидаем:

HTTP/2 301
location: https://example.ru/new-url

И отключайте автоматическое следование redirect при тесте

Иначе инструмент покажет конечный:

200

и разработчик может не заметить, что по дороге были:

302
→ 301
→ 301.

HTTP-тесты очень хорошо автоматизируются

Например:

known page
→ 200

old permanent URL
→ 301

temporary route
→ 302

missing page
→ 404

permanently removed resource
→ 410

CI может проверять это при каждом релизе.


Пример тестового контракта

expect(
  await status('/expert/article')
).toBe(200);

expect(
  await status('/expert/old-article')
).toBe(301);

expect(
  await status('/expert/definitely-missing')
).toBe(404);

А затем отдельно проверяется:

Location header.

Но одного известного 404 недостаточно

Особенно у SPA.

Нужно сгенерировать случайный URL:

/seo-test-does-not-exist-a8f392

Почему?

Потому что заранее известный:

/404

может быть специально настроен правильно.

А случайный маршрут всё ещё:

200.

Это очень хороший production smoke test

После deployment:

GET /seo-test-random-UUID

ожидаем:

404.

Если пришёл:

200,

release потенциально создаёт soft 404.


То же самое полезно для старого redirect

GET /known-old-url

без автоматического follow.

Ожидаем:

301.

И:

Location = точный конечный URL.

Проверяем отсутствие redirect chains

Тест может пройти redirects самостоятельно.

Например:

old
↓
middle
↓
final

и зафиксировать:

redirect hops = 2.

Для большинства обычных миграций можно принять внутреннее правило:

redirect hops <= 1.

Это уже SEO as Code

Не:

После релиза глазами откроем пару страниц.

А:

HTTP semantics
=
автоматический контракт.

Какие URL должны быть в sitemap

Обычно:

финальные;
200;
indexable;
canonical.

Не нужно добавлять:

301;
302;
404;
410.

Sitemap должен описывать актуальное содержимое сайта.


Иначе появляется странный сигнал

Sitemap:

Пожалуйста, обходите этот URL.

Server:

404.

или:

301.

Лучше заранее удалить такой URL из генератора sitemap.


CMS должна обновлять sitemap автоматически

Если статья:

published

→ появляется.

Если:

deleted

→ исчезает.

Если slug изменился:

старый URL → 301
новый URL → sitemap.

Не нужно каждый раз вручную редактировать XML.


Что происходит после удаления страницы из поиска

Нельзя ожидать:

поставил 404
↓
через минуту URL исчез.

Поисковой системе сначала нужно снова посетить URL и обработать новый статус.

Поэтому некоторое время старый адрес ещё может быть известен системе.


Правильный статус уже настроен.

Нужно дать crawler время обновить состояние.


То же относится к 301

После миграции поисковой системе нужно:

обойти старый URL;
увидеть redirect;
обойти новый;
переобработать canonical signals.

Это не синхронная database transaction.


Яндекс рекомендует сохранять permanent redirect достаточно долго

В актуальной документации Яндекса для 301 указано сохранять redirect как минимум шесть месяцев, чтобы поисковые системы корректно обработали перенос.

На практике для ценных старых URL часто вообще нет причины удалять рабочий redirect, если он недорог в поддержке.


301 не нужен только «на пару недель для Google»

Пока:

старые ссылки;
закладки;
внешние публикации

продолжают существовать, redirect всё ещё приносит пользу пользователям.


HTTP-коды важны и для API, но SEO-контекст другой

Например:

/api/project/123

может возвращать:

404.

Но если API закрыт от поиска и не является публичной страницей, это не SEO-документ.

Не нужно автоматически пытаться индексировать каждый HTTP route сайта.


Нас интересует прежде всего публичный URL-space

Например:

/
/services/*
/expert/*
/portfolio/*
/prices

Именно там status semantics особенно важна для поискового индекса.


Что отображать пользователю на 404

Можно сделать страницу полезной:

Такой страницы больше нет.

Возможно, адрес изменился.

[Перейти в Экспертный центр]

Популярные материалы:
...

Это улучшает UX.

Но:

HTTP status = 404.

остаётся неизменным.


Не нужно бояться ссылок на 404-странице

Custom 404 может иметь:

меню;
логотип;
поиск;
ссылки;
footer.

Это совершенно нормально.

Статус и UI решают разные задачи.


А вот canonical на 404 обычно не спасает ситуацию

Например:

404 page
canonical → /

не превращает несуществующий URL в дубль главной.

Если документ не существует:

404/410

уже описывает нужное состояние.


Не используйте redirect только для того, чтобы «убрать 404 из отчёта»

Например система показывает:

500 старых URL → 404.

Разработчик автоматически создаёт:

500 redirects → /

Теперь отчёт визуально чище.

Но архитектура стала хуже.


Сначала классифицируйте каждый класс URL

Например:

старые slug с новыми аналогами
→ 301.
удалённые материалы без замены
→ 404/410.
опечатки
→ 404.
устаревшие category paths
с прямой новой категорией
→ 301.

Не:

всё → homepage.

404 и 410 не должны быть в robots.txt только ради удаления из индекса

Если поисковому роботу нужно узнать:

эта страница теперь 404/410,

он должен иметь возможность запросить URL и получить соответствующий статус.

Блокировка crawling может мешать ему увидеть новое состояние.


Особенно важно это после удаления ранее индексируемой страницы

Пусть робот получит:

404/410.

И сможет обновить индекс.


Отдельно про robots.txt самого сайта

Ошибка:

/robots.txt → 404

не всегда трактуется так же, как обычный удалённый документ, поэтому инфраструктурные endpoints нужно тестировать отдельно.

Главная мысль здесь проще:

не применяйте к системным ресурсам механически те же правила, что к контентным страницам.


Как выглядит здоровая схема удаления

Страница:

/expert/old

решено удалить.

Если есть замена:

/expert/old
↓ 301
/expert/new

Внутренние ссылки:

→ /expert/new.

Sitemap:

только /expert/new.

Если замены нет:

/expert/old
↓ 404/410.

Внутренние ссылки удалены или исправлены.

Sitemap больше не содержит URL.


Как выглядит нездоровая

/expert/old
↓ 200
«Статья удалена»

Sitemap:

всё ещё содержит /expert/old.

Внутренние ссылки:

всё ещё ведут туда.

Canonical:

→ /.

Здесь почти каждый слой противоречит другому.


HTTP-коды должны быть согласованы со всем SEO-контрактом

Для существующей страницы:

200
+
self-canonical
+
sitemap
+
internal links

Для permanently moved:

301
+
old URL удалён из sitemap
+
internal links обновлены
+
new URL = 200.

Для deleted:

404/410
+
нет в sitemap
+
нет внутренних ссылок.

Для temporary redirect:

302
+
есть понятная причина временности
+
исходный URL остаётся частью архитектуры.

Очень полезна таблица URL-policy

Например в проекте:

Тип URLHTTP
Опубликованная статья200
Неизвестная статья404
Permanently deleted410
Старый slug301
Временный preview redirect302
Сервис временно недоступен503

Это уже можно превратить в automated tests.


Что мониторить в production

Очень полезно считать:

200 rate;
301 rate;
302 rate;
404 rate;
5xx rate.

Но сами цифры нужно интерпретировать.


Рост 404 может означать

сломанные внутренние ссылки;
внешний бот перебирает мусорные URL;
после deployment исчезли реальные routes;
удалена большая группа страниц.

Это совершенно разные ситуации.


Поэтому нужна разбивка по route group

Например:

404 /expert/*

резко выросли после релиза.

Это серьёзно.

А:

404 /wp-admin/*

на Node.js-сайте могут просто означать автоматическое сканирование ботами.


Точно так же внезапный рост 302 может обнаружить ошибку

Предполагалось:

301.

Но framework после обновления начал отдавать:

302.

Поэтому метрики статусов полезны не только backend-разработчику, но и SEO.


После каждого deployment полезен небольшой smoke-test

Например:

/                         → 200
/expert/known-article     → 200
/expert/old-slug          → 301
/definitely-does-not-exist → 404
/robots.txt               → expected
/sitemap.xml              → 200

Несколько запросов способны поймать очень серьёзный regression.


Например ошибка SPA fallback

После обновления Nginx:

/definitely-does-not-exist

внезапно:

404 → 200.

Smoke-test сразу падает.

До того, как Google или Яндекс успеют накопить тысячи soft 404.


Или ошибка старого URL

Ожидалось:

301

получили:

302.

Deployment можно остановить ещё до принятия релиза.


Это и есть смысл SEO как кода

Статус страницы не должен зависеть от того, заметил ли разработчик надпись в DevTools.

Он является проверяемым invariant.


Что показывают поисковые инструменты

Google Search Console способен показывать:

Not found (404);
Soft 404;
Page with redirect;
Server error.

Эти состояния полезны как внешний взгляд Google на сайт.

Но искать причину всё равно нужно на уровне реального HTTP и rendering.


Яндекс Вебмастер также диагностирует HTTP-проблемы

В частности:

404;
redirects;
server errors;
soft 404-подобные ошибки.

И рекомендует проверять реальный серверный код для несуществующих страниц.


Главная ошибка — начинать лечение с SEO-текстов

Представим статья исчезла из поиска.

Команда:

переписывает TITLE;
добавляет H2;
увеличивает текст;

А реальная проблема:

HTTP 302 → /login.

Никакое количество текста не исправит HTTP-контракт.


Поэтому диагностика начинается с самого нижнего уровня

DNS
↓
HTTP
↓
status
↓
headers
↓
HTML
↓
canonical/noindex
↓
content
↓
indexing

Не наоборот.


Практическая схема принятия решения

Страница больше не открывается по старому URL.

Сначала спрашиваем:

Есть ли у содержимого новый постоянный URL?

Если да:

301.

Если нет:

Страница временно находится в другом месте и вернётся?

Если да:

302.

Если нет:

Ресурс сознательно удалён навсегда и система это знает?

Если да:

410.

В остальных обычных случаях отсутствующего ресурса:

404.

А если UI показывает ошибку, но ответ:

200

исправляем:

soft 404.

Схема в одном блоке

             URL существует?
                /       \
              да         нет
              │           │
            200       Есть замена?
                        /      \
                      да        нет
                      │          │
                Постоянная?   Удалён
                  /     \      навсегда?
                да      нет     /    \
                │        │    да      нет
               301      302   410     404

И отдельно:

UI говорит «нет страницы»
+
HTTP 200
=
soft 404

Чего не нужно делать

Не перенаправлять каждый 404 на главную

Это не делает главную заменой отсутствующего документа.


Не использовать 302 для постоянных миграций

Если решение постоянное — и HTTP должен говорить об этом.


Не делать 404 для временного server outage

Страница не исчезла.

Сервис временно не способен её отдать.


Не возвращать 200 для красивой страницы ошибки

Дизайн не заменяет HTTP.


Не держать redirected и deleted URLs в sitemap

Sitemap должен описывать актуальные документы.


Не оставлять внутренние ссылки на redirect и 404

Redirect нужен прежде всего для внешней истории и старых адресов, а не как постоянная часть собственной навигации.


Не использовать 410 как «магический ускоритель SEO»

Используйте его, когда семантика действительно:

ресурс удалён окончательно.

Практический production checklist

Перед релизом стоит автоматически проверить:

  • индексируемые публичные страницы возвращают 200;
  • случайный несуществующий URL возвращает настоящий 404;
  • custom 404 сохраняет HTTP 404;
  • permanently deleted URLs возвращают выбранный проектом 404 или 410;
  • старые URL с прямой заменой возвращают 301;
  • временные redirects действительно возвращают 302/307, а не используются вместо permanent migration;
  • permanent redirects ведут сразу на конечный 200;
  • нет лишних redirect chains;
  • redirect target не возвращает 404, noindex или ещё один ошибочный redirect;
  • удалённые и redirected URL отсутствуют в Sitemap;
  • внутренние ссылки ведут сразу на конечные URL;
  • сбой БД/API не превращает существующие страницы в 404;
  • temporary server outage имеет server-error semantics, а не «страница исчезла»;
  • SPA fallback не создаёт 200 на случайных несуществующих маршрутах;
  • Search Console и Яндекс Вебмастер не показывают неожиданный рост soft 404 и HTTP errors.

Если такой контракт проверяется автоматически, большая часть SEO-проблем со статусами исчезает ещё до production.


Почему это особенно важно для динамических сайтов

На статическом сайте:

50 HTML-файлов.

Состояние каждого относительно понятно.

В динамической системе URL формируются из:

БД;
CMS;
slug;
route;
status;
permissions;

Одна ошибка общего router способна изменить статус:

10 000 URL.

Например редакционная система

Статья:

draft

не должна вести себя так же, как:

published.

Можно определить явную политику.

Например:

published
→ 200
deleted
→ 410
unknown slug
→ 404
old slug
→ 301 current slug

Теперь HTTP является отражением business state.


Это намного лучше универсального

всё → 200 index.html.

HTTP-код можно считать частью state machine контента

Например:

DRAFT
   ↓ publish
PUBLISHED
   ↓ rename
OLD_URL → 301
NEW_URL → 200
   ↓ delete
DELETED → 410

Тогда SEO-поведение становится прямым следствием жизненного цикла материала.


Это особенно хорошо тестируется

Создаём статью.

Ожидаем:

200.

Меняем slug.

Ожидаем:

old → 301
new → 200.

Удаляем.

Ожидаем:

410.

Именно так SEO перестаёт быть набором ручных настроек.


Что действительно важно запомнить

301 не означает:

Хороший redirect для SEO.

Он означает:

Перенос постоянный.

302:

Перенос временный.

404:

Такой ресурс не найден.

410:

Ресурс удалён окончательно.

200:

Ресурс существует и успешно отдан.

Soft 404:

Сервер сказал 200, но сама страница фактически говорит обратное.

Если выбирать статус именно по этому смыслу, большая часть SEO-решений становится удивительно простой.


Вместо вывода

Поисковой системе не нужен красивый интерфейс ошибки.

Ей нужен честный ответ сервера.

Если страница существует:

200.

Если переехала навсегда:

301.

Если переместилась временно:

302.

Если отсутствует:

404.

Если сознательно удалена навсегда:

410.

Если сервер показывает:

«Страница не найдена»

но отправляет:

200,

это уже soft 404 — противоречие между техническим состоянием URL и содержимым страницы.

Именно такие противоречия особенно опасны для современных SPA, CMS и динамических редакционных систем.

Правильную архитектуру лучше строить не вокруг вопроса:

Какой код больше нравится Google?

А вокруг другого:

Что на самом деле произошло с этим ресурсом?

Существует?
Перенесён?
Временно перенесён?
Удалён?
Временно недоступен?

После этого HTTP-код выбирается почти автоматически.

А затем вся остальная SEO-система должна подтверждать тот же смысл:

HTTP
+
canonical
+
sitemap
+
internal links
+
indexing policy.

Когда эти сигналы согласованы, поисковому роботу не приходится угадывать состояние сайта.

И техническое SEO становится значительно предсказуемее.

Есть похожая задача?

Опишите продукт, интеграции и ограничения. До разработки зафиксируем объём, риски и критерии приёмки.

Обсудить проект →