SEO для Headless CMS: настройка метаданных в Strapi, Contentful, Sanity

Последние пять лет я погружен в Headless CMS. Моя страсть это превращать технические сложности в понятные решения. Сегодня я хочу поделиться опытом, как настраивать SEO в Strapi, Contentful и Sanity, чтобы ваш контент не просто существовал, а доминировал в поиске. Речь пойдет о динамических OG-тегах, канонических URL и хитростях пагинации через API. Погнали!

Почему Headless CMS это вызов для SEO?

Headless CMS дают свободу: фронтенд отделен от бэкенда, контент доставляется через API, а дизайн не ограничен шаблонами. Но именно эта гибкость становится ловушкой для SEO. Если метаданные, OG-теги или канонические ссылки настроены неправильно, поисковые роботы просто не поймут, как индексировать ваш сайт.

Мой подход: контролировать каждую деталь через API и предварительный рендеринг. Расскажу, как это работает в трех популярных CMS.

Динамические OG-теги

OG-теги (Open Graph) — это то, как ваш сайт выглядит в соцсетях. Если они статичны, вы теряете вовлечение. Решение — динамические OG-теги, которые меняются для каждой страницы.

Strapi: Плагины + кастомные контроллеры

В Strapi я использую плагин strapi-plugin-seo, но часто дополняю его кастомным кодом. Например, для блога:

  1. Создаю поле meta_image в коллекции статей.
  2. В контроллере добавляю логику: если meta_image не задан, подставляю обложку статьи.
  3. Использую шаблоны (например, в Next.js), чтобы рендерить теги на основе данных из API:
javascript
// Пример для Next.js
export async function getServerSideProps({ params }) {
  const article = await fetch(`/api/articles/${params.slug}`);
  return {
    props: {
      meta: {
        ogTitle: article.title + " | Блог",
        ogImage: article.meta_image || article.cover.url,
      },
    },
  };
}

Contentful: Поля-шаблоны и Webhooks

В Contentful я создаю отдельное поле SEO Template типа JSON. В нем прописываю структуру OG-тегов с переменными вроде {title}.
При публикации контента срабатывает вебхук на мой сервер, который парсит шаблон, подставляет данные и обновляет метаданные через API.

Sanity: GROQ-запросы и Portable Text

Sanity хорош своей гибкостью. Для OG-тегов я использую GROQ-запросы, чтобы получать данные даже из вложенных объектов:

javascript
// Запрос для получения OG-данных
const query = `*[_type == "post" && slug.current == $slug][0]{
  title,
  "ogImage": metaImage.asset->url,
  excerpt
}`;

А затем передаю результат в компонент <Head> в React или Vue.

Канонические URL: Как избежать дублей

Канонические теги — ваш щит против дублированного контента. Но в Headless CMS их легко испортить, особенно если фронтенд рендерит страницы на клиенте.

Правила, которые я соблюдаю:

  1. Абсолютные ссылки, а не относительные.
  2. Динамическое определение канонического URL на основе хоста и пути.
  3. Игнорирование параметров сортировки и фильтров (если они не меняют контент).

Реализация в Strapi:

Добавляю middleware в API, который добавляет каноническую ссылку в ответ:

javascript
// middleware/canonical.js
module.exports = (req, res, next) => {
  const canonical = `https://${req.headers.host}${req.path}`;
  res.setHeader('Link', `<${canonical}>; rel="canonical"`);
  next();
};

В Contentful и Sanity:

Использую фронтенд-фреймворки (например, Nuxt.js), чтобы динамически вставлять тег <link rel="canonical"> в шапку страницы. Важно проверять, что URL генерируется корректно при SSR (Server-Side Rendering).

Пагинация через API

Пагинация в Headless CMS часто ломает SEO, особенно если реализована через бесконечный скролл или параметры ?page=2. Вот как я это решаю.

Стратегия:

  • Использовать HTTP-заголовки Link с rel="next" и rel="prev".
  • Добавлять канонические ссылки на первую страницу пагинации.
  • Для бесконечного скролла — пререндерить первые N страниц.

Пример для Strapi:

В контроллере списка статей возвращаю заголовки:

javascript
// api/article/controllers/article.js
const page = parseInt(ctx.query.page) || 1;
const nextPage = page + 1;
ctx.set('Link', `<https://example.com/articles?page=${nextPage}>; rel="next"`);

В Sanity и Contentful:

Использую параметры limit и offset в API-запросах и передаю их в фронтенд. Например, в Gatsby для пререндеринга страниц:

javascript
// gatsby-node.js
const articles = await sanityClient.fetch(`*[_type == "post"]`);
const perPage = 10;
const pages = Math.ceil(articles.length / perPage);
Array.from({ length: pages }).forEach((_, i) => {
  createPage({
    path: i === 0 ? `/blog` : `/blog/${i + 1}`,
    component: blogTemplate,
    context: {
      skip: i * perPage,
    },
  });
});

Инструменты, которые меня спасают

  • Screaming Frog. Для аудита канонических URL и метатегов после деплоя.
  • Swagger/OpenAPI. Документирую эндпоинты метаданных, чтобы не запутаться.
  • Google Structured Data Testing Tool. Проверяю, как роботы видят OG-теги.

Главный секрет в том, чтобы не надеяться на CMS, а взять контроль в свои руки. Настраивайте метаданные через API, тестируйте каждую страницу и помните даже в Headless-архитектуре SEO начинается с вашего кода.

Если у вас есть кейсы или вопросы пишите в комментарии.