SEO в Nuxt: useSeoMeta и useHead

SSR отдаёт готовый HTML, но без правильных мета-тегов он бесполезен для SEO: useSeoMeta задаёт title, описание и превью для соцсетей.
Суть: useSeoMeta — типобезопасный композабл для SEO-мета-тегов (title, description, Open Graph). useHead — общий инструмент для любых тегов head. При SSR теги попадают в исходный HTML, поэтому видны поисковикам и ботам соцсетей.

Мы начали курс с того, что SSR нужен ради SEO. Но сам по себе серверный HTML — это полдела. Поисковику и соцсетям нужны правильные мета-теги: заголовок страницы, описание, картинка превью. Без них даже идеально отрендеренная страница плохо индексируется и некрасиво выглядит при шаринге в мессенджер.

Современный и рекомендуемый способ — useSeoMeta. Это плоский типобезопасный API специально для SEO:

<script setup>
useSeoMeta({
  title: "Каталог книг — Мой магазин",
  description: "Большой выбор книг с доставкой",
  ogTitle: "Каталог книг",
  ogDescription: "Большой выбор книг с доставкой",
  ogImage: "/og/catalog.png",
})
</script>

Для динамических страниц мета-теги строят из данных. На странице товара заголовок берут из самого товара — и поскольку это происходит при SSR, корректный <title> уже есть в исходном HTML:

<script setup>
const { data: product } = await useFetch("/api/products/" + id)
useSeoMeta({
  title: () => product.value.name + " — Магазин",
  description: () => product.value.shortDescription,
})
</script>

useHead — более общий инструмент: им добавляют любые теги <head> (например, link, script, canonical). Правило простое: для SEO-полей берите useSeoMeta, для всего остального — useHead.

   Зачем теги при SSR

   запрос -> SSR рендерит страницу + useSeoMeta
        |
        v
   HTML с <title>, <meta description>, og:* УЖЕ внутри
        |
        +-- поисковик читает -> индексирует корректно
        +-- бот мессенджера читает og:* -> красивое превью

Безопасность серверной части — это не один приём, а слоёная защита. Нижний слой — валидация и санитизация любого входа, чтобы кривые или вредоносные данные не дошли до логики. Слой выше — аутентификация (кто этот пользователь) и авторизация (что ему можно), которые удобно централизовать в серверном middleware. Поверх — ограничение частоты запросов от одного клиента, чтобы эндпоинт нельзя было завалить перебором. И наконец, секреты живут только на сервере и никогда не пересекают границу в браузер. Ни один из слоёв не заменяет другой; вместе они и составляют надёжный бэкенд.

Как работает под капотом

За тегами head в Nuxt стоит библиотека Unhead. useSeoMeta — это удобная обёртка с типами поверх неё: вы пишете плоские поля (ogImage вместо ручного <meta property="og:image">), а Unhead собирает корректные теги. При SSR они рендерятся прямо в <head> исходного HTML — поэтому доступны ботам, не исполняющим JS. При клиентской навигации теги реактивно обновляются. Функции-геттеры (() => ...) делают теги реактивными к данным.

Смоделируем сборку мета-тегов из плоского объекта — упрощённую логику useSeoMeta:

// Плоские SEO-поля -> теги для <head>.
function buildMeta(seo) {
  const tags = [];
  if (seo.title) tags.push("<title>" + seo.title + "</title>");
  if (seo.description)
    tags.push('<meta name="description" content="' + seo.description + '">');
  if (seo.ogTitle)
    tags.push('<meta property="og:title" content="' + seo.ogTitle + '">');
  if (seo.ogImage)
    tags.push('<meta property="og:image" content="' + seo.ogImage + '">');
  return tags.join("\n");
}

const product = { name: "Книга про Vue", shortDescription: "Лучший гид по Vue" };
const head = buildMeta({
  title: product.name + " — Магазин",
  description: product.shortDescription,
  ogTitle: product.name,
  ogImage: "/og/book.png",
});
console.log("В <head> попадёт:\n" + head);

Попробуй сам ▶ — плоские поля превращаются в готовые теги. При SSR эта разметка уже в исходном HTML, и её видят боты.

Частые ошибки

  • Ставить мета-теги только на клиенте. Если задать их после монтирования, бот без JS их не увидит. Делайте при SSR.
  • Забывать Open Graph. Без og:* ссылка в мессенджере выглядит голо, без картинки и описания.
  • Одинаковый title на всех страницах. Уникальный заголовок на страницу — база SEO.

Best practices

  • SEO-поля — через useSeoMeta; прочие теги head — через useHead.
  • На динамических страницах стройте теги из данных через геттеры () => ....
  • Всегда задавайте Open Graph (ogTitle, ogDescription, ogImage) для красивого шаринга.

Итог: SSR даёт HTML, а useSeoMeta наполняет его правильными мета-тегами, видимыми поисковикам и соцсетям при первом же запросе. Дальше — стратегии рендеринга и подготовка к деплою.

Проверьте себя
1. Почему мета-теги через useSeoMeta нужно задавать при SSR, а не только на клиенте?
AТак быстрее работает CSS
BПри SSR теги попадают в исходный HTML и видны поисковикам и ботам, не исполняющим JavaScript
CuseSeoMeta не работает в браузере
DКлиентские теги занимают больше памяти
2. Какой композабл рекомендуется для SEO-полей вроде title и Open Graph?
AuseHead для всего подряд
BuseSeoMeta — плоский типобезопасный API специально для SEO-мета-тегов
CuseFetch
DuseState