Полное руководство по настройке SEO на Next.js

15 апреля 2026 г.

Next.js — это фреймворк React с мощной поддержкой SEO из коробки, но для достижения максимальной видимости в поисковых системах требуется дополнительная настройка. В этом руководстве ты узнаешь, как правильно управлять мета-тегами, структурированными данными, динамическими маршрутами, sitemap и производительностью, чтобы твой Next.js проект получал максимум органического трафика.

Управление метаданными в Next.js

С выходом Next.js 13 появились два подхода к работе с метаданными: классический Pages Router и новый App Router. Выбор зависит от версии и архитектуры твоего проекта.

Метаданные в App Router

В App Router используется экспорт статического или динамического объекта metadata из файла layout.tsx или page.tsx. Это самый современный и рекомендуемый способ.

// app/layout.tsx
export const metadata = {
  title: {
    default: 'Мой сайт',
    template: '%s | Мой сайт'
  },
  description: 'Описание сайта для поисковых систем',
  keywords: 'nextjs, seo, react',
  robots: 'index, follow',
  viewport: 'width=device-width, initial-scale=1'
}

export default function RootLayout({ children }) {
  return (
    <html lang="ru">
      <body>{children}</body>
    </html>
  )
}

Объект metadata поддерживает все основные теги: title, description, robots, canonical, alternates, openGraph, twitter и другие. Шаблон %s в title.template автоматически подставляет заголовок дочерней страницы.

Метаданные в Pages Router

Если ты используешь Pages Router, метаданные добавляются через компонент Head из next/head.

// pages/index.js
import Head from 'next/head'

export default function Home() {
  return (
    <>
      <Head>
        <title>Главная страница</title>
        <meta name="description" content="Описание главной страницы" />
        <meta name="robots" content="index, follow" />
        <link rel="canonical" href="https://example.com/" />
      </Head>
      {/* контент */}
    </>
  )
}

Для динамического управления метатегами на каждой странице используй next/head внутри компонента страницы. В отличие от App Router, здесь нет автоматического наследования — нужно повторять общие теги на каждой странице или создать компонент-обёртку.

Динамические метаданные

В реальных проектах заголовки и описания часто зависят от данных — например, от содержимого статьи или товара. В App Router для этого используется функция generateMetadata.

// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }): Promise<Metadata> {
  const post = await getPostBySlug(params.slug)
  
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: [post.coverImage]
    },
    alternates: {
      canonical: `https://example.com/blog/${params.slug}`
    }
  }
}

Функция выполняется на сервере при каждом запросе (или во время сборки для статических страниц). Это позволяет генерировать уникальные метатеги для каждой страницы на основе внешних данных.

В Pages Router динамика реализуется через getServerSideProps или getStaticProps с последующей передачей данных в Head.

Open Graph и Twitter Cards

Open Graph (OG) отвечает за то, как твой сайт выглядит при публикации в соцсетях (Facebook, VK, LinkedIn, Telegram). Twitter использует отдельный набор тегов, но поддерживает и OG.

В App Router добавь поле openGraph в объект metadata:

export const metadata = {
  title: 'Заголовок статьи',
  openGraph: {
    title: 'Заголовок для соцсетей',
    description: 'Краткое описание для превью',
    url: 'https://example.com/article',
    siteName: 'Мой сайт',
    images: [
      {
        url: 'https://example.com/og-image.jpg',
        width: 1200,
        height: 630,
        alt: 'Превью статьи'
      }
    ],
    type: 'article',
    publishedTime: '2025-04-15T00:00:00Z',
    authors: ['Автор']
  },
  twitter: {
    card: 'summary_large_image',
    title: 'Заголовок для Twitter',
    description: 'Описание для Twitter',
    images: ['https://example.com/twitter-image.jpg']
  }
}

Рекомендуемый размер изображения для OG — 1200×630 пикселей, формат JPEG или PNG. Без указания OG-тегов соцсети будут использовать случайные элементы страницы, что часто выглядит непрезентабельно.

Структурированные данные (JSON-LD)

JSON-LD — это формат разметки, помогающий поисковым системам лучше понимать содержимое страницы: статьи, товары, рецепты, отзывы, организации. Next.js рекомендует встраивать JSON-LD через компонент Script с type="application/ld+json" и стратегией beforeInteractive или afterInteractive.

Пример для статьи (Article Schema):

// app/blog/[slug]/page.tsx
import Script from 'next/script'

export default function BlogPost({ post }) {
  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: post.title,
    description: post.excerpt,
    image: post.coverImage,
    datePublished: post.date,
    author: {
      '@type': 'Person',
      name: post.author
    },
    publisher: {
      '@type': 'Organization',
      name: 'Мой сайт',
      logo: {
        '@type': 'ImageObject',
        url: 'https://example.com/logo.png'
      }
    }
  }

  return (
    <>
      <Script
        id="article-jsonld"
        type="application/ld+json"
        strategy="afterInteractive"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* контент статьи */}
    </>
  )
}

Используй валидатор структурированных данных от Google (Rich Results Test), чтобы убедиться в корректности разметки. Ошибки в JSON-LD не нарушат работу страницы, но могут помешать поисковым системам использовать расширенные сниппеты.

Генерация sitemap и robots.txt

Карта сайта (sitemap) помогает поисковым роботам находить все страницы, особенно если сайт имеет динамическую структуру. В Next.js App Router sitemap генерируется автоматически через специальный файл.

Создай app/sitemap.ts:

// app/sitemap.ts
import { MetadataRoute } from 'next'

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const baseUrl = 'https://example.com'
  
  // Получи список всех статей из CMS или БД
  const posts = await getAllPosts()
  
  const postUrls = posts.map((post) => ({
    url: `${baseUrl}/blog/${post.slug}`,
    lastModified: post.updatedAt,
    changeFrequency: 'weekly',
    priority: 0.8
  }))
  
  return [
    {
      url: baseUrl,
      lastModified: new Date(),
      changeFrequency: 'daily',
      priority: 1.0
    },
    ...postUrls
  ]
}

Файл robots.txt создаётся в app/robots.ts:

// app/robots.ts
import { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
      disallow: ['/admin/', '/api/']
    },
    sitemap: 'https://example.com/sitemap.xml'
  }
}

В Pages Router используются сторонние библиотеки, например next-sitemap, которая генерирует статические файлы во время сборки.

Оптимизация производительности для SEO

С 2021 года Google использует Core Web Vitals (LCP, INP, CLS) как фактор ранжирования. Next.js предоставляет несколько инструментов для автоматической оптимизации.

1. Встроенная оптимизация изображений через компонент next/image автоматически ресайзит, сжимает и использует современные форматы (WebP, AVIF).

import Image from 'next/image'

export default function Page() {
  return (
    <Image
      src="/photo.jpg"
      width={1200}
      height={630}
      alt="Описание изображения"
      priority // для LCP-изображений
    />
  )
}

2. Динамический импорт для отложенной загрузки некритичных компонентов:

const HeavyComponent = dynamic(() => import('@/components/Heavy'), {
  loading: () => <p>Загрузка...</p>,
  ssr: false
})

3. Предзагрузка ссылок через компонент Link с пропом prefetch (включен по умолчанию в production).

4. Минимизация размера страницы — Next.js автоматически расщепляет код (code splitting) и добавляет только необходимый JavaScript для каждой страницы.

Используй Lighthouse в Chrome DevTools для проверки Core Web Vitals на реальных страницах. Даже при идеальном SEO медленный сайт будет проигрывать конкурентам.

Типичные ошибки и как их избежать

Ошибка 1: Дублирование метатегов — в App Router не смешивай экспорт metadata с компонентом Head из next/head. Используй только один подход.

Ошибка 2: Отсутствие канонических URL — если один контент доступен по нескольким адресам (например, с трекерными параметрами), укажи canonical в метаданных, чтобы избежать дублей в индексе.

Ошибка 3: Клиентская загрузка контента — если важный текст подгружается через useEffect или fetch на клиенте, поисковый робот может его не увидеть. Используй серверные компоненты или getServerSideProps/generateStaticParams.

Ошибка 4: Игнорирование мобильных устройств — Google индексирует в первую очередь мобильную версию. Убедись, что сайт корректно отображается на экранах от 320px, используй адаптивный дизайн.

Ошибка 5: Отсутствие страницы 404 — создай кастомную страницу not-found.tsx в App Router или 404.js в Pages Router. Это улучшает пользовательский опыт и сигнализирует поисковым системам о битых ссылках.

После внедрения всех настроек проверь результат в Google Search Console — отправь sitemap, посмотри на покрытие индексации и ошибки робота. SEO — это непрерывный процесс, требующий мониторинга и адаптации под изменения алгоритмов.