Стек проекта: Next.js SSR, Cloudflare, интеграция с открытым API. Продукт в бете. Команда небольшая, документации по деплою я не видел. Всё что ниже — то что видно снаружи, из ответов сервера и HTML-заголовков.
Next.js SSR: 3 ошибки настройки, которые ломают SEO
Next.js на бумаге SEO-дружественный. На практике команды ловят одни и те же три ошибки — не потому что глупые, а потому что документация не выделяет эти пункты жирным. Разбор из аудита SaaS-платформы для DeFi-аналитики. Черный ящик, без доступа к коду. Что вижу извне и как это чинится.
1. Рассинхрон slug-маппинга: 404-контент под HTTP 200
Первое, что нашёл — критично: топ-5 самых крупных сущностей отрасли открывались как «Not found for slug: X». HTTP-статус страницы при этом — 200 OK.
Что это значит:
- Пользователь заходит с гугла, видит красный алерт «данных нет», уходит. Bounce rate растёт.
- Google получает 200 OK, индексирует страницу как рабочую. Через месяц замечает что содержимое «пустое», начинает деранжировать домен целиком.
- Sitemap-парсер добавляет несуществующие протоколы в очередь на индексирование.
Причина, судя по симптомам, — slug в БД не совпадает с каноническим именем. Пример: сущность записана как «aave-v3», а посетитель пришёл по /protocol/aave. Роутинг находит запись «нет», рендерит error-компонент, но обёртка сохраняет HTTP 200.
Как чинить:
- Слой алиасов:
aave → aave-v3,uniswap → uniswap-v3и т.д. Простая маппинг-таблица. - Когда сущности нет — вернуть 404 или 410 честно. В Next.js это
notFound()изnext/navigation. - 301-редирект на канонический slug при попадании на алиас.
2. Дублирующие meta robots на страницах ошибок
Открываю 404-страницу — в <head> два тега:
<meta name="robots" content="noindex"> <meta name="robots" content="index, follow">
Одновременно и «не индексируй», и «индексируй». Спецификация не описывает такую ситуацию — поведение поисковика зависит от реализации: обычно применяется более рестриктивное правило (noindex), но не гарантированно.
Причина — в Next.js App Router глобальный layout ставит один robots, а конкретный error-компонент добавляет второй. Оба остаются в DOM.
Как чинить:
- Использовать
metadataиgenerateMetadataв компоненте страницы. Next.js сам мёржит и не дублирует. - Проверить что глобальный layout не устанавливает robots силой через inline JSX.
- Автотест на CI: HTML-парсер, считает количество
<meta name="robots">— если больше одного, падает билд.
3. Отсутствие security.txt и PWA-манифест с одной иконкой
Две мелочи, но обе видны каждому, кто проверяет:
Нет /.well-known/security.txt. Файл описан в RFC 9116 — стандарт того, как white-hat хакер должен сообщать об уязвимости. Для финансовой платформы (DeFi-аналитика) отсутствие security.txt читается как «мы не готовы к responsible disclosure». Bug bounty программы платформ (HackenProof, Immunefi) требуют этот файл при листинге.
Как чинить: положить public/.well-known/security.txt с содержимым:
Contact: mailto:security@yourdomain.com Expires: 2027-01-01T00:00:00Z Preferred-Languages: en Canonical: https://yourdomain.com/.well-known/security.txt
PWA-манифест с одной иконкой 32×32. Файл manifest.json объявляет иконки для установки как PWA. Одна маленькая иконка — на Android при попытке «Добавить на главный экран» приложение получит размытый значок. На iOS вообще не установится.
Как чинить: добавить иконки 192×192, 512×512, apple-touch-icon 180×180. Работа на 15 минут через любой PWA-генератор (PWABuilder.com даёт весь пакет).
Что ещё стоит проверить в SSR-проекте перед продом
Не входило в разбор, но встречается регулярно:
- sitemap.xml генерируется, но лежит устаревшим. Проверьте: он должен собираться при билде или на request-time из БД, а не быть статичным файлом.
- Canonical на страницах пагинации. Аналогично WordPress-проблеме — каждая страница списка должна иметь canonical на саму себя.
- Cache-Control для HTML. Cloudflare по умолчанию не кэширует HTML — нужно явно настроить или использовать stale-while-revalidate.
- Метрики Core Web Vitals. Cloudflare Web Analytics или Real User Monitoring в самом Next.js. Без цифр — неясно что улучшать.
- 404 без layout. Иногда 404-страница рендерится без глобальных footer/menu — пользователь не может вернуться на главную.
Как это встроить в процесс, чтобы не ловить снова
Все три ошибки — конфигурационные. Их можно отловить автоматически:
- Pre-commit hook: Playwright пробегает по 5-10 ключевым URL, проверяет HTTP статус, количество meta robots, наличие Open Graph, наличие security.txt. Если ошибка — коммит блокируется.
- CI после деплоя: Lighthouse CI с бюджетом по Core Web Vitals. Регрессия >10% — билд красный.
- Alerting: периодический прогон по прод-URL, скрины в Slack при отклонениях. Работа на день, экономия — постоянно.
Свой Next.js-проект перед релизом?
Напишите в @ai_agentura — сделаю блиц-аудит с чек-листом. Асинхронно, без созвонов.