Вы можете написать идеальный код, но если поисковые системы не могут его прочитать — сайт останется невидимым. Техническое SEO — это мост между вашей разработкой и поисковиками.
- Frontend и backend разработчики
- DevOps-инженеры
- Tech Lead и CTO
- Full-stack разработчики
В этом гайде мы разберем все технические аспекты SEO с точки зрения разработки: от скорости загрузки до структурированных данных. Никакой воды — только код, метрики и конкретные решения.
Что вы узнаете:
- ✅ Как измерить и улучшить Core Web Vitals
- ✅ Правильная настройка индексации и robots.txt
- ✅ Реализация структурированных данных (Schema.org)
- ✅ Оптимизация скорости загрузки (от 1с до 300мс)
- ✅ Mobile-first индексация и адаптивность
- ✅ Настройка HTTPS, HTTP/2, Brotli
- ✅ Создание оптимальной архитектуры URL
- ✅ Работа с JavaScript-фреймворками (React, Vue, Angular)
Core Web Vitals: метрики, которые важны Google
С 2021 года Google официально использует Core Web Vitals как фактор ранжирования. Это три ключевые метрики производительности:
1. LCP (Largest Contentful Paint) — Скорость загрузки контента
Что это: Время до загрузки самого большого видимого элемента (обычно изображение или текстовый блок).
Допустимо: 2.5 - 4 секунд
Плохо: > 4 секунд
Как улучшить LCP:
<!-- 1. Preload критических ресурсов -->
<link rel="preload" as="image" href="/hero-image.webp" fetchpriority="high">
<link rel="preload" as="font" href="/fonts/inter-bold.woff2" type="font/woff2" crossorigin>
<!-- 2. Используйте современные форматы изображений -->
<picture>
<source srcset="/hero.avif" type="image/avif">
<source srcset="/hero.webp" type="image/webp">
<img src="/hero.jpg" alt="Hero image" loading="eager" fetchpriority="high">
</picture>
<!-- 3. Lazy loading для остальных изображений -->
<img src="/product.webp" alt="Product" loading="lazy" width="600" height="400">
/* 1. Inline критический CSS в */
/* styles-critical.css */
body {
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
}
.hero {
min-height: 600px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
/* 2. Асинхронная загрузка некритического CSS */
/* Вставьте в HTML: */
/* */
/* 3. Избегайте @import в CSS (блокирует рендеринг) */
/* ❌ Плохо: @import url('typography.css'); */
/* ✅ Хорошо: используйте в HTML */
# 1. Включите Brotli сжатие (лучше Gzip)
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml;
# 2. HTTP/2 для параллельной загрузки
listen 443 ssl http2;
# 3. Кэширование статики
location ~* \.(jpg|jpeg|png|webp|avif|gif|svg|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 4. Gzip для старых браузеров
gzip on;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript;
2. FID (First Input Delay) — Интерактивность
Что это: Время от первого взаимодействия пользователя (клик, tap) до реакции браузера.
Как улучшить FID:
// 1. Разбивайте длинные задачи на части
// ❌ Плохо: блокирует поток на 500ms
function processLargeArray(items) {
items.forEach(item => {
// Тяжелые вычисления
processItem(item);
});
}
// ✅ Хорошо: разбиваем на чанки
async function processLargeArrayOptimized(items, chunkSize = 100) {
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize);
chunk.forEach(item => processItem(item));
// Даем браузеру "передохнуть"
await new Promise(resolve => setTimeout(resolve, 0));
}
}
// 2. Используйте Web Workers для тяжелых вычислений
// worker.js
self.addEventListener('message', (e) => {
const result = heavyCalculation(e.data);
self.postMessage(result);
});
// main.js
const worker = new Worker('worker.js');
worker.postMessage(data);
worker.addEventListener('message', (e) => {
console.log('Результат:', e.data);
});
// 3. Debounce для событий (scroll, resize, input)
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
window.addEventListener('scroll', debounce(() => {
// Обработка скролла
}, 150));
// 1. Code Splitting с React.lazy
import React, { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Загрузка...</div>}>
<HeavyComponent />
</Suspense>
);
}
// 2. Мемоизация для предотвращения лишних рендеров
import { memo, useMemo, useCallback } from 'react';
const ExpensiveComponent = memo(({ data }) => {
const processedData = useMemo(() => {
return data.map(item => expensiveOperation(item));
}, [data]);
const handleClick = useCallback(() => {
console.log('Clicked');
}, []);
return <div onClick={handleClick}>{processedData}</div>;
});
// 3. Виртуализация длинных списков (react-window)
import { FixedSizeList } from 'react-window';
function VirtualizedList({ items }) {
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{({ index, style }) => (
<div style={style}>{items[index]}</div>
)}
</FixedSizeList>
);
}
3. CLS (Cumulative Layout Shift) — Стабильность визуала
Что это: Сумма всех неожиданных сдвигов контента во время загрузки страницы.
Как исправить CLS:
<!-- 1. Всегда указывайте width и height для изображений -->
<img src="product.jpg" alt="Product" width="800" height="600" loading="lazy">
<!-- 2. Для адаптивных изображений используйте aspect-ratio -->
<style>
.responsive-image {
width: 100%;
height: auto;
aspect-ratio: 16 / 9; /* Резервирует пространство */
}
</style>
<img src="banner.jpg" class="responsive-image" alt="Banner">
<!-- 3. Резервируйте место для динамического контента -->
<div style="min-height: 200px;">
<!-- Здесь загрузится контент через AJAX -->
</div>
<!-- 4. Используйте font-display для веб-шрифтов -->
<style>
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom.woff2') format('woff2');
font-display: swap; /* Показываем системный шрифт пока грузится */
}
</style>
/* 1. Skeleton screens для загрузки контента */
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
border-radius: 4px;
}
@keyframes loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
/* 2. Резервирование места для баннеров/рекламы */
.ad-container {
min-height: 250px; /* Стандартный размер баннера */
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
}
/* 3. Фиксированные размеры для критических элементов */
.hero-section {
height: 600px; /* Или 100vh, но фиксированное значение */
}
/* 4. Transform вместо margin/padding для анимаций */
/* ❌ Плохо: вызывает layout shift */
.element:hover {
margin-top: -10px;
}
/* ✅ Хорошо: не влияет на layout */
.element:hover {
transform: translateY(-10px);
}
Скорость загрузки: от 3 секунд до 300 миллисекунд
Скорость напрямую влияет на конверсию: задержка в 1 секунду снижает конверсию на 7%. Google это знает и учитывает скорость при ранжировании.
Инструменты для анализа скорости
Чек-лист оптимизации скорости:
- Минификация: HTML, CSS, JavaScript (Webpack, Vite, Parcel)
- Сжатие: Gzip или Brotli на сервере
- Кэширование: Browser cache + CDN
- CDN: Cloudflare, CloudFront, Fastly
- Оптимизация изображений: WebP/AVIF, lazy loading, responsive images
- Критический CSS: Inline critical CSS, defer остального
- Async/Defer для скриптов: Не блокируем рендеринг
- Tree shaking: Удаление неиспользуемого кода
- Code splitting: Разбивка бандлов на чанки
- HTTP/2 или HTTP/3: Параллельная загрузка ресурсов
// webpack.config.js
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
mode: 'production',
// 1. Code Splitting
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10
},
common: {
minChunks: 2,
priority: 5,
reuseExistingChunk: true
}
}
},
// 2. Минификация
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // Удаляем console.log в продакшене
}
}
}),
new CssMinimizerPlugin()
]
},
// 3. Brotli compression
plugins: [
new CompressionPlugin({
filename: '[path][base].br',
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8
})
],
// 4. Source maps только для продакшена
devtool: 'source-map',
// 5. Оптимизация изображений
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // Inline images < 8kb
}
},
use: [
{
loader: 'image-webpack-loader',
options: {
mozjpeg: { progressive: true, quality: 85 },
optipng: { enabled: true },
pngquant: { quality: [0.65, 0.90], speed: 4 },
webp: { quality: 85 }
}
}
]
}
]
}
};
Индексация и краулинг: управление ботами
Даже идеально оптимизированный сайт бесполезен, если поисковые боты не могут его проиндексировать. Разберем правильную настройку robots.txt, sitemap и meta-тегов.
1. robots.txt — Правила для ботов
# Базовая конфигурация robots.txt
# Разрешаем всем ботам
User-agent: *
Allow: /
# Запрещаем индексацию служебных страниц
Disallow: /admin/
Disallow: /api/
Disallow: /temp/
Disallow: /*.json$
Disallow: /*?*utm_source= # Блокируем дубли с UTM
# Разрешаем доступ к CSS/JS (важно для Google)
Allow: /css/
Allow: /js/
Allow: /*.css$
Allow: /*.js$
# Специальные правила для конкретных ботов
User-agent: Googlebot
Crawl-delay: 0
User-agent: Yandex
Crawl-delay: 1
Host: https://example.com # Главное зеркало для Яндекса
# Ссылка на sitemap
Sitemap: https://example.com/sitemap.xml
Sitemap: https://example.com/sitemap-images.xml
Sitemap: https://example.com/sitemap-news.xml
- ❌ Блокировка CSS/JS (Google не видит дизайн сайта)
- ❌ Disallow: / на продакшене (случайно забыли убрать)
- ❌ Неправильный синтаксис (Disallow вместо Allow)
- ❌ Блокировка важных страниц (например, /products/)
2. Sitemap.xml — Карта сайта
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
<!-- Главная страница -->
<url>
<loc>https://example.com/</loc>
<lastmod>2024-12-16</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
<!-- Страница товара с изображением -->
<url>
<loc>https://example.com/products/laptop-dell</loc>
<lastmod>2024-12-15</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
<image:image>
<image:loc>https://example.com/images/laptop-dell.webp</image:loc>
<image:caption>Ноутбук Dell XPS 15</image:caption>
</image:image>
</url>
<!-- Новостная статья -->
<url>
<loc>https://example.com/blog/seo-guide</loc>
<lastmod>2024-12-16T10:30:00+00:00</lastmod>
<news:news>
<news:publication>
<news:name>Example Blog</news:name>
<news:language>ru</news:language>
</news:publication>
<news:publication_date>2024-12-16T10:30:00+00:00</news:publication_date>
<news:title>SEO для программистов: полный гайд</news:title>
</news:news>
</url>
</urlset>
// generate-sitemap.js
const { SitemapStream, streamToPromise } = require('sitemap');
const { createWriteStream } = require('fs');
const { Readable } = require('stream');
async function generateSitemap() {
const links = [
{ url: '/', changefreq: 'daily', priority: 1.0 },
{ url: '/about/', changefreq: 'monthly', priority: 0.7 },
{ url: '/blog/', changefreq: 'weekly', priority: 0.8 },
// ... получаем из БД
];
const stream = new SitemapStream({ hostname: 'https://example.com' });
const writeStream = createWriteStream('./public/sitemap.xml');
streamToPromise(Readable.from(links).pipe(stream))
.then((data) => {
writeStream.write(data.toString());
writeStream.end();
console.log('✅ Sitemap сгенерирован');
});
}
generateSitemap();
// Добавьте в package.json:
// "scripts": {
// "generate:sitemap": "node generate-sitemap.js"
// }
3. Meta-теги для управления индексацией
<!-- 1. Robots meta-тег -->
<!-- Разрешаем индексацию и переход по ссылкам -->
<meta name="robots" content="index, follow">
<!-- Запрещаем индексацию (для служебных страниц) -->
<meta name="robots" content="noindex, nofollow">
<!-- Индексируем, но не переходим по ссылкам -->
<meta name="robots" content="index, nofollow">
<!-- Запрещаем кэширование (для динамического контента) -->
<meta name="robots" content="noarchive, nocache">
<!-- Запрещаем показ сниппета в поиске -->
<meta name="robots" content="nosnippet">
<!-- 2. Googlebot-specific -->
<meta name="googlebot" content="index, follow, max-snippet:-1, max-image-preview:large">
<!-- 3. Canonical (избегаем дублей) -->
<link rel="canonical" href="https://example.com/page/">
<!-- 4. Alternate для мультиязычности -->
<link rel="alternate" hreflang="en" href="https://example.com/en/page/">
<link rel="alternate" hreflang="ru" href="https://example.com/ru/page/">
<link rel="alternate" hreflang="x-default" href="https://example.com/en/page/">
Structured Data (Schema.org): Rich Snippets
Структурированные данные помогают поисковикам лучше понять содержимое страницы и показывать расширенные сниппеты (рейтинги, цены, FAQ и т.д.).
- ⭐ Рейтинги и отзывы в сниппетах (CTR +30%)
- 💰 Цены товаров прямо в поиске
- 📅 Даты событий и расписание
- ❓ FAQ-блоки в результатах поиска
- 🍞 Хлебные крошки (breadcrumbs)
- 📊 Таблицы с данными
Основные типы разметки Schema.org
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "SEO для программистов: техническая оптимизация",
"description": "Полное руководство по технической SEO",
"image": "https://example.com/images/seo-guide.jpg",
"author": {
"@type": "Person",
"name": "Илья",
"url": "https://example.com/author/dmitry"
},
"publisher": {
"@type": "Organization",
"name": "Example Tech Blog",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/logo.png"
}
},
"datePublished": "2024-12-16T10:00:00+03:00",
"dateModified": "2024-12-16T14:30:00+03:00",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://example.com/blog/seo-for-developers"
}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Ноутбук Dell XPS 15",
"image": [
"https://example.com/images/dell-xps-1.jpg",
"https://example.com/images/dell-xps-2.jpg"
],
"description": "Мощный ноутбук для разработки",
"sku": "DELL-XPS-15-2024",
"brand": {
"@type": "Brand",
"name": "Dell"
},
"offers": {
"@type": "Offer",
"url": "https://example.com/products/dell-xps-15",
"priceCurrency": "RUB",
"price": "125000",
"priceValidUntil": "2024-12-31",
"availability": "https://schema.org/InStock",
"seller": {
"@type": "Organization",
"name": "Example Store"
}
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
"reviewCount": "127"
},
"review": [
{
"@type": "Review",
"author": {
"@type": "Person",
"name": "Иван Петров"
},
"datePublished": "2024-11-20",
"reviewBody": "Отличный ноутбук для программирования",
"reviewRating": {
"@type": "Rating",
"ratingValue": "5",
"bestRating": "5"
}
}
]
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Что такое Core Web Vitals?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Core Web Vitals — это набор из трех метрик производительности: LCP (скорость загрузки), FID (интерактивность) и CLS (стабильность визуала). Google использует их как фактор ранжирования с 2021 года."
}
},
{
"@type": "Question",
"name": "Как улучшить LCP?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Основные способы: 1) Оптимизация изображений (WebP/AVIF), 2) Preload критических ресурсов, 3) Использование CDN, 4) Минификация CSS/JS, 5) Включение Brotli сжатия."
}
}
]
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Главная",
"item": "https://example.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Блог",
"item": "https://example.com/blog/"
},
{
"@type": "ListItem",
"position": 3,
"name": "SEO",
"item": "https://example.com/blog/seo/"
},
{
"@type": "ListItem",
"position": 4,
"name": "SEO для программистов",
"item": "https://example.com/blog/seo/seo-for-developers"
}
]
}
</script>
Инструменты для проверки Schema.org
Mobile-First индексация
С 2019 года Google использует mobile-first индексацию — это значит, что ранжирование определяется по мобильной версии сайта, даже для десктопных запросов.
Чек-лист мобильной оптимизации:
<!-- 1. Обязательный viewport meta-тег -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 2. Theme color для Android Chrome -->
<meta name="theme-color" content="#10b981">
<!-- 3. Apple touch icon -->
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<!-- 4. Web App Manifest -->
<link rel="manifest" href="/manifest.json">
/* 1. Mobile-first подход */
/* Базовые стили для мобильных */
.container {
padding: 16px;
font-size: 16px; /* Минимум 16px чтобы iOS не зумил */
}
/* Затем адаптируем под десктоп */
@media (min-width: 768px) {
.container {
padding: 32px;
max-width: 1200px;
margin: 0 auto;
}
}
/* 2. Touch-friendly кнопки (минимум 48x48px) */
.button {
min-height: 48px;
min-width: 48px;
padding: 12px 24px;
font-size: 16px;
}
/* 3. Адаптивная типографика */
body {
font-size: clamp(16px, 2vw, 18px);
line-height: 1.6;
}
h1 {
font-size: clamp(28px, 5vw, 48px);
}
/* 4. Отключаем hover на touch устройствах */
@media (hover: hover) {
.button:hover {
background: #059669;
}
}
/* 5. Ориентация экрана */
@media (orientation: landscape) {
.hero {
height: 80vh;
}
}
@media (orientation: portrait) {
.hero {
height: 100vh;
}
}
{
"name": "Example App",
"short_name": "Example",
"description": "Пример Progressive Web App",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#10b981",
"orientation": "portrait-primary",
"icons": [
{
"src": "/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Структура URL и внутренняя перелинковка
Правильная структура URL влияет на юзабилити, индексацию и ранжирование. Разберем best practices.
Правила построения SEO-friendly URL:
| Правило | ❌ Плохо | ✅ Хорошо |
|---|---|---|
| Читаемость | /product?id=12345 | /products/dell-xps-15 |
| Длина | /categories/electronics/computers/laptops/gaming/asus-rog/ | /laptops/asus-rog-strix |
| Спецсимволы | /Blog_Post!@2024#NEW | /blog/post-2024 |
| Регистр | /Products/Dell-XPS-15 | /products/dell-xps-15 |
| Trailing slash | /about и /about/ | Выберите один вариант |
| Дубли | www и без www | Один canonical |
// Правильная структура маршрутов
const express = require('express');
const router = express.Router();
// 1. RESTful URL структура
// ✅ Хорошо: семантичные URL
router.get('/products', getAllProducts);
router.get('/products/:slug', getProductBySlug);
router.get('/categories/:category/products', getProductsByCategory);
router.get('/blog/:year/:month/:slug', getBlogPost);
// ❌ Плохо: query параметры для основного контента
// router.get('/page?id=123&type=product')
// 2. Редирект со старых URL на новые
router.get('/old-url', (req, res) => {
res.redirect(301, '/new-url'); // 301 = постоянный редирект
});
// 3. Удаление trailing slash (или добавление - выберите одно)
app.use((req, res, next) => {
if (req.path.slice(-1) === '/' && req.path.length > 1) {
const query = req.url.slice(req.path.length);
res.redirect(301, req.path.slice(0, -1) + query);
} else {
next();
}
});
// 4. Lowercase URLs
app.use((req, res, next) => {
if (req.path !== req.path.toLowerCase()) {
res.redirect(301, req.path.toLowerCase());
} else {
next();
}
});
// 5. Генерация slug из заголовка
function generateSlug(title) {
return title
.toLowerCase()
.replace(/[^а-яёa-z0-9\s-]/g, '') // Удаляем спецсимволы
.trim()
.replace(/\s+/g, '-') // Пробелы -> дефисы
.replace(/-+/g, '-'); // Убираем повторяющиеся дефисы
}
// Пример: "SEO для программистов!" -> "seo-dlya-programmistov"
# 1. Редирект с www на без www (или наоборот)
server {
server_name www.example.com;
return 301 https://example.com$request_uri;
}
# 2. Редирект с HTTP на HTTPS
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
# 3. Trailing slash redirect
rewrite ^/(.*)/$ /$1 permanent;
# 4. Редирект старых URL на новые
location /old-blog/ {
return 301 /blog/;
}
location ~ ^/product/(\d+)$ {
return 301 /products/$1;
}
# 5. Склейка дублей параметров
if ($args ~* "^(.*)&utm_source=(.*)$") {
return 301 $uri?$1;
}
HTTPS и безопасность
HTTPS — это не просто безопасность, но и прямой фактор ранжирования Google с 2014 года. Сайты без HTTPS помечаются как "небезопасные" в браузерах.
Настройка HTTPS:
server {
listen 443 ssl http2;
server_name example.com;
# 1. SSL сертификат (Let's Encrypt бесплатный)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# 2. Современные протоколы (отключаем старые небезопасные)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
# 3. HSTS (заставляет браузер всегда использовать HTTPS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# 4. Дополнительные заголовки безопасности
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 5. Content Security Policy
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline';" always;
# 6. OCSP Stapling (ускоряет SSL handshake)
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}
# 1. Установка Certbot
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx
# 2. Получение сертификата
sudo certbot --nginx -d example.com -d www.example.com
# 3. Автоматическое обновление (добавить в cron)
sudo certbot renew --dry-run
# 4. Добавить в crontab для автопродления
sudo crontab -e
# Добавить строку:
# 0 0 * * * certbot renew --quiet --post-hook "systemctl reload nginx"
JavaScript SEO (React, Vue, Angular)
Современные SPA (Single Page Applications) на React/Vue/Angular создают особые вызовы для SEO, так как контент генерируется на клиенте с помощью JavaScript.
Решения для JavaScript SEO:
1. Server-Side Rendering (SSR)
// pages/blog/[slug].js
import Head from 'next/head';
export default function BlogPost({ post }) {
return (
<>
<Head>
<title>{post.title} | My Blog</title>
<meta name="description" content={post.excerpt} />
<meta property="og:title" content={post.title} />
<meta property="og:description" content={post.excerpt} />
<meta property="og:image" content={post.image} />
<link rel="canonical" href={`https://example.com/blog/${post.slug}`} />
</Head>
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
</>
);
}
// Этот метод выполняется на сервере при каждом запросе
export async function getServerSideProps({ params }) {
const res = await fetch(`https://api.example.com/posts/${params.slug}`);
const post = await res.json();
return {
props: { post }
};
}
// Или для статики используйте getStaticProps + getStaticPaths
2. Static Site Generation (SSG)
// Генерация статических страниц на этапе сборки (самое быстрое)
export async function getStaticProps({ params }) {
const post = await fetchPost(params.slug);
return {
props: { post },
revalidate: 3600 // Обновлять каждый час (ISR - Incremental Static Regeneration)
};
}
export async function getStaticPaths() {
const posts = await fetchAllPosts();
const paths = posts.map(post => ({
params: { slug: post.slug }
}));
return {
paths,
fallback: 'blocking' // Генерировать новые страницы по запросу
};
}
3. Prerendering для SPA
# 1. React Snap (генерирует статический HTML)
npm install --save-dev react-snap
# package.json
{
"scripts": {
"build": "react-scripts build",
"postbuild": "react-snap"
},
"reactSnap": {
"include": [
"/",
"/about",
"/blog"
]
}
}
# 2. Prerender.io (облачный сервис)
# Определяет ботов и отдает им pre-rendered HTML
# 3. Rendertron (self-hosted решение от Google)
docker run -p 3000:3000 rendertron
Полный чек-лист технического SEO
Соберем все важные технические факторы в один чек-лист, который можно использовать при аудите или запуске нового проекта.
🚀 Производительность и Core Web Vitals
- LCP < 2.5 секунд (оптимизация изображений, preload)
- FID < 100ms (оптимизация JavaScript, code splitting)
- CLS < 0.1 (фиксированные размеры, skeleton screens)
- TTFB < 600ms (быстрый сервер, CDN)
- Включено Brotli или Gzip сжатие
- HTTP/2 или HTTP/3
- Минификация HTML, CSS, JS
- Критический CSS inline в
<head> - Async/defer для некритических скриптов
- WebP или AVIF для изображений
- Lazy loading для изображений и iframe
- CDN для статических ресурсов
- Browser caching (expires headers)
🔍 Индексация и краулинг
- Корректный robots.txt (не блокирует важные страницы)
- XML sitemap сгенерирован и отправлен в Google/Яндекс
- Sitemap обновляется автоматически при добавлении контента
- Meta robots настроены корректно (index/noindex)
- Canonical URL для избежания дублей
- Нет ошибок 404 на важных страницах
- 301 редиректы настроены для старых URL
- Pagination правильно размечена (rel="next/prev" или View All)
- Google Search Console настроен
- Яндекс.Вебмастер настроен
📱 Mobile-First
- Viewport meta tag присутствует
- Адаптивный дизайн (responsive или adaptive)
- Touch-friendly элементы (минимум 48x48px)
- Шрифты минимум 16px (чтобы iOS не зумил)
- Нет горизонтального скролла
- Быстрая загрузка на 3G (< 5 секунд)
- Mobile usability errors исправлены (Search Console)
🔐 Безопасность
- HTTPS включен на всем сайте
- SSL сертификат валиден и не истек
- HSTS header включен
- Mixed content отсутствует (нет HTTP на HTTPS)
- Security headers настроены (X-Frame-Options, CSP и т.д.)
- TLS 1.2 или выше
🏗️ Структура и архитектура
- SEO-friendly URL (короткие, читаемые, lowercase)
- Логическая иерархия страниц (не глубже 3-4 кликов от главной)
- Breadcrumbs (хлебные крошки) на всех страницах
- Внутренняя перелинковка настроена
- Нет битых ссылок (проверка через Screaming Frog)
- Редиректы не цепочками (A→B→C), а напрямую (A→C)
- Trailing slash единообразно (везде или нигде)
📊 Structured Data (Schema.org)
- JSON-LD разметка для ключевых страниц
- Organization schema на главной
- Article schema для статей
- Product schema для товаров
- BreadcrumbList для навигации
- FAQPage для страниц с вопросами
- Review/AggregateRating для отзывов
- Разметка проверена в Rich Results Test
🎯 JavaScript SEO
- Контент доступен без JavaScript (SSR или prerendering)
- Meta-теги рендерятся на сервере
- Нет бесконечного скролла без пагинации
- History API используется для навигации
- Lazy loading не скрывает важный контент
🌍 Международный SEO
- Hreflang для мультиязычных сайтов
- Правильная структура (поддомены, подпапки или ccTLD)
- Lang атрибут в HTML тегах
- Локализованный контент (не машинный перевод)
Мониторинг и аналитика
Настройка техническую SEO — это только начало. Важно постоянно мониторить метрики и оперативно реагировать на проблемы.
<!-- Google Analytics 4 -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX', {
'send_page_view': false // Для SPA вручную отслеживаем
});
// Для SPA отслеживание переходов
window.addEventListener('routechange', () => {
gtag('event', 'page_view', {
page_path: window.location.pathname
});
});
</script>
<!-- Яндекс.Метрика -->
<script type="text/javascript">
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
ym(XXXXXXXX, "init", {
clickmap:true,
trackLinks:true,
accurateTrackBounce:true,
webvisor:true
});
</script>
// monitor-seo.js - Скрипт для мониторинга SEO метрик
const axios = require('axios');
const cheerio = require('cheerio');
async function checkSEO(url) {
const issues = [];
try {
const response = await axios.get(url);
const $ = cheerio.load(response.data);
// 1. Проверка title
const title = $('title').text();
if (!title) {
issues.push('❌ Отсутствует title');
} else if (title.length < 30 || title.length > 60) {
issues.push(`⚠️ Title некорректной длины: ${title.length} символов`);
}
// 2. Проверка meta description
const description = $('meta[name="description"]').attr('content');
if (!description) {
issues.push('❌ Отсутствует meta description');
} else if (description.length < 120 || description.length > 160) {
issues.push(`⚠️ Description некорректной длины: ${description.length}`);
}
// 3. Проверка h1
const h1Count = $('h1').length;
if (h1Count === 0) {
issues.push('❌ Отсутствует h1');
} else if (h1Count > 1) {
issues.push(`⚠️ Найдено ${h1Count} тегов h1 (должен быть один)`);
}
// 4. Проверка canonical
const canonical = $('link[rel="canonical"]').attr('href');
if (!canonical) {
issues.push('⚠️ Отсутствует canonical');
}
// 5. Проверка Open Graph
const ogTitle = $('meta[property="og:title"]').attr('content');
const ogDescription = $('meta[property="og:description"]').attr('content');
const ogImage = $('meta[property="og:image"]').attr('content');
if (!ogTitle || !ogDescription || !ogImage) {
issues.push('⚠️ Неполная разметка Open Graph');
}
// 6. Проверка изображений без alt
const imagesWithoutAlt = $('img:not([alt])').length;
if (imagesWithoutAlt > 0) {
issues.push(`⚠️ ${imagesWithoutAlt} изображений без alt`);
}
// 7. Проверка внутренних ссылок без протокола
const relativeLinks = $('a[href^="/"]').length;
const absoluteLinks = $('a[href^="http"]').length;
console.log(`\n📊 Отчет для: ${url}`);
console.log(`✅ Title: ${title}`);
console.log(`✅ H1: ${$('h1').first().text()}`);
console.log(`📝 Description: ${description?.substring(0, 50)}...`);
console.log(`🔗 Внутренних ссылок: ${relativeLinks}`);
console.log(`🌐 Внешних ссылок: ${absoluteLinks}`);
if (issues.length > 0) {
console.log('\n⚠️ Найдены проблемы:');
issues.forEach(issue => console.log(issue));
} else {
console.log('\n✅ SEO проблем не обнаружено');
}
} catch (error) {
console.error('❌ Ошибка проверки:', error.message);
}
}
// Использование
checkSEO('https://example.com');
// Автоматический мониторинг каждый час
setInterval(() => {
checkSEO('https://example.com');
}, 3600000);
Инструменты для технического SEO
⚡ Скорость и производительность
🛠️ Разработка и тестирование
🚀 Нужна помощь с техническим SEO?
Проведу полный технический аудит вашего сайта и составлю план оптимизации с приоритетами
Заказать аудитЗаключение
Техническое SEO — это фундамент, без которого даже отличный контент не будет ранжироваться. Основные тезисы:
- Core Web Vitals — это не просто метрики, а фактор ранжирования
- Mobile-first — оптимизация под мобильные обязательна в 2024
- HTTPS — базовое требование, без вариантов
- JavaScript SEO — используйте SSR или prerendering для SPA
- Structured Data — увеличивает CTR через rich snippets
- Мониторинг — настройте автоматические проверки и алерты
Начните с чек-листа выше — пройдитесь по каждому пункту для вашего сайта. Исправьте критические проблемы (HTTPS, индексация, Core Web Vitals), затем переходите к оптимизации.
Техническое SEO — это не разовая задача, а постоянный процесс. Технологии меняются, алгоритмы обновляются, поэтому важно следить за новостями и регулярно проводить аудиты.
Отличная статья! Особенно полезен раздел про Core Web Vitals. Применил рекомендации по LCP на нашем проекте — время загрузки упало с 4.2 до 1.8 секунд. Использовал preload для hero-изображения и перешли на WebP. Позиции в Google выросли на 15% за месяц! 🚀