vibeblame

Как добавить SEO и мета-теги

Title, description, Open Graph, canonical, robots.txt — что каждый из них делает и как добавить в Next.js, Astro, Vue (Nuxt), Angular, WordPress и Tilda.

Что делает каждый тег

ТегНа что влияет
<title>Вкладка браузера, заголовок в результатах Google
<meta name="description">Текст сниппета в Google (иногда)
<h1>Главный сигнал о теме страницы для поисковиков
og:title / og:description / og:imageПревью ссылки в Telegram, Slack, iMessage, Twitter
<link rel="canonical">Говорит Google, какой URL считать «настоящим»
robots.txtУправляет тем, какие страницы индексируют поисковики

Оптимальная длина: title 30–60 символов, description 120–160 символов. Длиннее Google обрезает.


Next.js (App Router)

Статические страницы:

// app/page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Заголовок страницы',
  description: 'О чём эта страница, примерно 150 символов. Будьте конкретны.',
  openGraph: {
    title: 'Заголовок страницы',
    description: 'То же или похожее на meta description',
    images: [{ url: '/og-image.png', width: 1200, height: 630, alt: 'Описание' }],
    url: 'https://yoursite.com',
    type: 'website',
  },
  alternates: {
    canonical: 'https://yoursite.com',
  },
}

Динамические страницы:

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

Шаблон заголовка (в layout):

// app/layout.tsx
export const metadata: Metadata = {
  title: {
    default: 'My App',
    template: '%s | My App',
  },
}

robots.txt:

// app/robots.ts
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
  return {
    rules: { userAgent: '*', allow: '/', disallow: ['/api/', '/admin/'] },
    sitemap: 'https://yoursite.com/sitemap.xml',
  }
}

Astro

---
// src/layouts/BaseLayout.astro
const { title, description, canonicalURL, ogImage } = Astro.props
---
<html>
  <head>
    <title>{title}</title>
    <meta name="description" content={description} />
    <link rel="canonical" href={canonicalURL} />
    <meta property="og:title" content={title} />
    <meta property="og:description" content={description} />
    <meta property="og:image" content={ogImage ?? '/og-image.png'} />
    <meta property="og:url" content={canonicalURL} />
    <meta property="og:type" content="website" />
  </head>
  <body><slot /></body>
</html>

Используйте на страницах:

---
import BaseLayout from '../layouts/BaseLayout.astro'
---
<BaseLayout
  title="Заголовок страницы"
  description="Примерно 150 символов о том, что на этой странице."
  canonicalURL="https://yoursite.com"
>
  <h1>Заголовок страницы</h1>
</BaseLayout>

Для robots.txt создайте public/robots.txt:

User-agent: *
Allow: /
Sitemap: https://yoursite.com/sitemap.xml

Vue (Nuxt)

Nuxt 3 предоставляет composables useSeoMeta() и useHead() с полноценной поддержкой SSR — мета-теги рендерятся на сервере и сразу видны всем поисковым роботам.

Статические мета на странице:

<!-- pages/index.vue -->
<script setup>
useSeoMeta({
  title: 'Заголовок страницы',
  description: 'Примерно 150 символов о том, что на этой странице.',
  ogTitle: 'Заголовок страницы',
  ogDescription: 'То же или похожее на description.',
  ogImage: 'https://yoursite.com/og-image.png',
  ogUrl: 'https://yoursite.com',
  ogType: 'website',
})

useHead({
  link: [{ rel: 'canonical', href: 'https://yoursite.com' }],
})
</script>

Динамические мета из API:

<!-- pages/blog/[slug].vue -->
<script setup>
const route = useRoute()
const { data: post } = await useFetch(`/api/posts/${route.params.slug}`)

useSeoMeta({
  title: () => post.value?.title,
  description: () => post.value?.excerpt,
  ogTitle: () => post.value?.title,
  ogDescription: () => post.value?.excerpt,
  ogImage: () => post.value?.coverImage,
})

useHead({
  link: [{ rel: 'canonical', href: `https://yoursite.com/blog/${route.params.slug}` }],
})
</script>

Глобальные настройки по умолчанию в nuxt.config.ts:

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      titleTemplate: '%s | My Site',
      meta: [
        { name: 'description', content: 'Описание сайта по умолчанию' },
      ],
    },
  },
})

robots.txt — создайте public/robots.txt:

User-agent: *
Allow: /
Sitemap: https://yoursite.com/sitemap.xml

Или используйте модуль @nuxtjs/robots для динамического управления.


Angular

Angular предоставляет сервисы Title и Meta из @angular/platform-browser:

// src/app/home/home.component.ts
import { Component, OnInit } from '@angular/core'
import { Title, Meta } from '@angular/platform-browser'

@Component({ selector: 'app-home', templateUrl: './home.component.html' })
export class HomeComponent implements OnInit {
  constructor(private title: Title, private meta: Meta) {}

  ngOnInit() {
    this.title.setTitle('Заголовок страницы')
    this.meta.updateTag({ name: 'description', content: 'Примерно 150 символов.' })
    this.meta.updateTag({ property: 'og:title', content: 'Заголовок страницы' })
    this.meta.updateTag({ property: 'og:description', content: 'Примерно 150 символов.' })
    this.meta.updateTag({ property: 'og:image', content: 'https://yoursite.com/og-image.png' })
    this.meta.updateTag({ property: 'og:url', content: 'https://yoursite.com' })
  }
}

Canonical-ссылка (добавляется вручную):

import { Inject } from '@angular/core'
import { DOCUMENT } from '@angular/common'

constructor(@Inject(DOCUMENT) private doc: Document) {}

setCanonical(url: string) {
  let link: HTMLLinkElement =
    this.doc.querySelector('link[rel="canonical"]') || this.doc.createElement('link')
  link.setAttribute('rel', 'canonical')
  link.setAttribute('href', url)
  this.doc.head.appendChild(link)
}

Важно: мета-теги, установленные через JavaScript, не видны большинству краулеров без Angular Universal (SSR). Google умеет выполнять JS, но превью в Telegram, Slack, Twitter и многие другие боты — нет. Для полноценного SEO добавьте SSR:

ng add @angular/ssr

С SSR мета-теги рендерятся прямо в HTML-ответе и сразу доступны всем краулерам.

robots.txt — разместите в src/ и добавьте в assets в angular.json:

"assets": ["src/favicon.ico", "src/assets", "src/robots.txt"]

src/robots.txt:

User-agent: *
Allow: /
Sitemap: https://yoursite.com/sitemap.xml

WordPress

Проще всего — плагин. Он добавляет все нужные теги через панель администратора без правки кода:

  • Yoast SEO — самый популярный вариант. Добавляет title, description, OG-теги, canonical, sitemap и robots.txt из отдельного раздела настроек.
  • Rank Math — схожий функционал, немного легче.

После установки заполните SEO-заголовок и описание для каждой страницы и записи. HTML-теги плагин добавит сам.

Без плагина добавьте в functions.php темы:

function add_custom_meta_tags() {
  if (is_singular()) {
    global $post;
    $description = get_post_meta($post->ID, '_meta_description', true);
    if ($description) {
      echo '<meta name="description" content="' . esc_attr($description) . '">' . PHP_EOL;
    }
    echo '<link rel="canonical" href="' . esc_url(get_permalink()) . '">' . PHP_EOL;
  }
}
add_action('wp_head', 'add_custom_meta_tags');

robots.txt в WordPress генерируется автоматически по адресу /robots.txt. Редактировать можно через Yoast SEO -> Инструменты -> Редактор файлов, или напрямую на сервере.


Tilda

В редакторе: Настройки сайта -> SEO — заполните глобальный заголовок, описание и OG-изображение.

Для отдельных страниц: настройки страницы (иконка шестерёнки) -> вкладка SEO -> заполните title, description и OG-изображение для каждой страницы.

Tilda генерирует все необходимые мета-теги из этих полей — код не нужен.

robots.txt: Настройки сайта -> SEO -> robots.txt.


H1 — один на страницу

На каждой странице должен быть ровно один <h1>. Это главный сигнал о теме для поисковиков.

<!-- Правильно -->
<h1>Чёткая тема страницы</h1>
<h2>Подтема</h2>

<!-- Неправильно — несколько h1 путают поисковики -->
<h1>Основной заголовок</h1>
<h1>Ещё один заголовок</h1>

Проверка

После — запустите vibeblame снова, SEO-скор должен вырасти.

Как добавить SEO и мета-теги | vibeblame