Метатеги Next.js в body: проверка девяти условий
Neaptide · 12 октября 2026 г. · 8 мин чтения
Проверили Next.js 15.5.22 в девяти условиях: исходный HTML, DOM браузера и положение canonical. Показываем результаты и границы выводов для SEO.
В этой статье

Метаданные после закрывающего тега head могут быть следствием потоковой выдачи Next.js. На нашем локальном стенде с production-сборкой асинхронные метаданные оказались в body для двух профилей запроса и в head для третьего. Значения присутствовали во всех полных ответах, но это ещё не доказывает, что поисковик принимает каждый тег в таком месте.
Сначала определите, какой тег проверяете, с каким профилем запроса и в каком представлении документа. Наш опыт объясняет выдачу метаданных. Для canonical дополнительно учитывайте требование Google к размещению в head: правильного URL где-либо в документе недостаточно, чтобы считать реализацию корректной.
Что мы проверили
12 октября 2026 года мы проверили Next.js 15.5.22 с React и React DOM 19.2.6 и Node v25.2.1. Три режима и три HTTP-профиля User-Agent дали девять условий. Каждое проверили дважды: сохранили 18 полных ответов HTTP 200, заголовки и HTML. В обоих прогонах совпали число тегов, значения и положение.
Сравнивали статический объект metadata, асинхронную generateMetadata со стандартной конфигурацией и ту же функцию с htmlLimitedBots: /.*/. Оба маршрута использовали force-dynamic. Статический здесь — способ задания метаданных, а не предварительная генерация страницы. В асинхронную функцию намеренно добавили задержку 250 мс без внешних запросов. Она позволяет увидеть последовательность выдачи, но не моделирует реальную нагрузку и не измеряет быстродействие.
Ответ сервера зависел от профиля запроса

| Режим метаданных | UA браузера | UA Googlebot | UA Twitterbot |
|---|---|---|---|
| Объект · по умолчанию | head | head | head |
| Async · по умолчанию | body | body | head |
| Async · блокирующий | head | head | head |
Коллектор передавал строку, похожую на Chrome 134, Googlebot/2.1 или Twitterbot/1.0. Это значения HTTP-заголовка, а не посещения соответствующих сервисов. Все запросы использовали Accept: text/html и Accept-Encoding: identity, без cookies и заголовков клиентской навигации. Точные строки и запросы есть в файле результатов.
Браузер оставил стандартные async-теги в body
Отдельно открыли три режима в подключённом браузере; полученный сервером User-Agent указывал Chrome 154. В DOM теги title, description и canonical находились в head в статическом и блокирующем режимах. В стандартном асинхронном режиме они остались в body: переноса в head мы не наблюдали. Эти три загрузки отделены от 18 HTTP-запросов и не являются проверкой рендеринга Googlebot.
| Режим | DOM: все три тега |
|---|---|
| Объект · по умолчанию | head |
| Async · по умолчанию | body |
| Async · блокирующий | head |
Для canonical нужно отдельное решение
Next.js описывает потоковую выдачу метаданных в body и блокирующую выдачу для ботов с ограниченной обработкой HTML. Это объясняет воспроизведённую картину. При этом Google указывает, что HTML-ссылка canonical принимается в head; пример добавления через JavaScript также использует document.head. Общая рекомендация фреймворка не разрешает вопрос о конкретном теге.
В нашем стандартном async-режиме canonical остаётся в body и в ответе сервера, и в DOM. Мы не можем утверждать, что Google принял или отклонил его: стенд работал локально, использовал noindex и не отправлялся на индексацию. Это повод проверить опубликованный маршрут. Если canonical остаётся вне head, выберите реализацию, соответствующую требованиям Google, и повторите проверку. Показанная ниже блокирующая конфигурация дала head на стенде; это проверенный способ размещения, а не доказанный рост позиций.
Как проверить свой маршрут
- Сохраните полный ответ production-страницы вместе с заголовками запроса и версией релиза. Проверяйте реальные элементы title, description и canonical: строка внутри React-скрипта не является ещё одним HTML-тегом.
- Отдельно проверьте DOM после рендеринга: значения, дубли и принадлежность к head или body. Правильный заголовок вкладки ничего не говорит о canonical.
- Сравните профили, важные для сервиса, в котором обнаружена проблема. Разделяйте браузер, подставленную строку робота и реальный запрос внешнего сервиса. Подмена User-Agent проверяет только выбор ответа вашим сервером.
- Примените требования к конкретному тегу. Для canonical проверьте опубликованную страницу доступными инструментами Google и сведения о канонизации, обеспечив размещение HTML-ссылки в head. Локальный DOM не показывает, какой canonical выбрал Google.
- Измените необходимую часть реализации, пересоберите сайт и повторите те же проверки. Если меняете потоковую выдачу метаданных, оцените загрузку на реальной нагрузке: искусственная задержка нашего стенда не даёт оценки скорости production-сайта.
Конфигурация, которую мы проверили
Для третьего режима пересобрали то же приложение с этой настройкой. Все три тега оказались в head при каждом проверенном профиле. Настройка заменяет стандартное правило сопоставления ботов; /.*/ подходит для любого User-Agent. При внедрении сохраните остальные параметры конфигурации и оцените область изменения.
// next.config.mjs
export default {
htmlLimitedBots: /.*/,
};Параметр меняет выдачу метаданных во всём приложении. Мы не проверяли отдельное регулярное выражение только для выбранных ботов, production-кеши или переходы между маршрутами. Этот опыт не обосновывает перевод всех сайтов на блокирующую выдачу.
Как повторить эксперимент
Скачайте стенд с lock-файлом зависимостей. В README приведены команды сборки и запуска обеих конфигураций, затем Python-коллектора. Два сервера запускаются в отдельных терминалах, коллектор — из корня распакованного архива. Задержка, noindex и зарезервированные адреса example.test предназначены только для локального опыта. В архиве доказательств — исходные HTTP-ответы и снимки DOM; в results.json — полные профили и разобранные наблюдения.
Данные для проверки
- Протокол, результаты и источники · JSON
- Методика и ограничения · TXT
- Стенд для повторения · ZIP
- Исходные HTTP-ответы и DOM · ZIP
Проверены одна версия, две локальные сборки и три загрузки в браузере. Мы не тестировали CDN, настоящих удалённых роботов, клиентскую навигацию, ошибки метаданных или Cache Components. Не измеряли индексацию, позиции, цитаты ИИ, трафик, конверсии и Core Web Vitals. Сбор и проверку выполнил один исследователь. Поведение своей версии и окружения нужно перепроверить.