Что делает каждый тег
| Тег | На что влияет |
|---|---|
<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>
Проверка
- OG-теги: вставьте URL на opengraph.xyz
- Отображение title и description: Google Rich Results Test
- Статус индексации: Google Search Console -> Проверка URL
После — запустите vibeblame снова, SEO-скор должен вырасти.