Недавно я выпустил две статьи - про подход POSSE и про SEO + GEO в 2026 году. В комментариях и личных сообщениях несколько человек попросили описать техническую часть.
Решил написать. Сразу оговорюсь, я не претендую на идеальное решение. Сайт не сделан безупречно - я знаю его ограничения и расскажу о них честно. Это просто разбор: что, как и почему сделано именно так.
Что это за сайт
"На Дерево" - это статический сайт с тремя функциями:
Главная страница проекта
Лендинг для продукта MENO
Блог
Сайт двуязычный - русский и английский. Никакого SPA, никакого серверного рендеринга. Чистая статика.

Стек
Что | Чем |
|---|---|
Фреймворк | Astro 6 (SSG-режим) |
Язык | TypeScript, strict-режим |
Контент блога | Astro Content Collections + Markdown |
Сервер | Nginx на VPS |
SSL | Certbot (Let's Encrypt) |
Аналитика | Яндекс.Метрика |
Стек простой. Это намеренно.
Почему Astro
Astro - SSG-фреймворк. Собирает статический HTML на этапе билда. На выходе - папка dist с файлами, которую можно положить куда угодно. Никакого рантайма на сервере, никакого Node.js в продакшне.
Для блога и лендинга это идеальная модель. Контент не меняется между деплоями. Отдавать статику через Nginx - быстрее и надёжнее, чем гонять SSR.
Content Collections в Astro позволяют описать схему контента и писать посты в Markdown / MDX. Типизация из коробки - если в фронтматтере не хватает обязательного поля, билд упадёт. Это удобно, когда у тебя десятки постов и два языка.
Два языка: как устроена локализация
Структура URL:
naderevo.com/ru/ - главная (русский) naderevo.com/en/ - главная (английский) naderevo.com/ru/blog/ - блог naderevo.com/en/blog/ - блог naderevo.com/ - redirect-entry
Корень / - служебная страница. Она не индексируется (noindex). Её единственная задача - определить язык пользователя по localStorage, cookie или заголовку браузера и перенаправить на /ru/ или /en/.
Конфигурация в Astro:
locales: ['ru', 'en'], prefixDefaultLocale: true, redirectToDefaultLocale: false
prefixDefaultLocale: true - оба языка живут с префиксом. Нет ситуации, когда русская версия на /, а английская на /en/. Оба равноправны. Это упрощает hreflang, canonical и вообще всю SEO-логику.
redirectToDefaultLocale: false - Astro не форсит редирект на дефолтный язык. Я сам решаю, куда отправить пользователя.
SEO-разметка
Каждая контентная страница содержит:
Базовые мета-теги
canonical- каноничный URL этой страницыhreflang- ссылки на все языковые версии (включаяx-default)og:title,og:description,og:image,og:url,og:localetwitter:card,twitter:title,twitter:description,twitter:image
Structured Data (JSON-LD)
Разные типы для разных страниц:
Страница | Schema.org тип |
|---|---|
Главная | WebSite |
Лендинг MENO | SoftwareApplication |
Статья блога | BlogPosting |
JSON-LD рендерится прямо в <head> на этапе сборки. Никаких клиентских скриптов для разметки.
Sitemap и robots.txt
// astro.config import sitemap from '@astrojs/sitemap';
@astrojs/sitemap генерирует карту сайта при билде. Я добавил фильтр, чтобы не попадали служебные страницы - тот же корневой / и подобные.
robots.txt максимально открытый:
User-agent: * Allow: / Sitemap: https://naderevo.com/sitemap-index.xml
Никаких блокировок для AI-краулеров. Я писал об этом в статье про SEO и GEO - если вы хотите, чтобы языковые модели знали о вашем контенте, не закрывайте от них сайт.
Аналитика и приватность
Яндекс.Метрика подключена, но с условием: она запускается только после явного согласия пользователя.
Cookie-consent
На сайте есть баннер с двумя кнопками - принять и отклонить. Без хитростей.
Решение пользователя сохраняется в cookie и localStorage. Если человек нажал "отклонить":
Скрипт Метрики не загружается
Существующие cookie и записи в
localStorageот Метрики очищаются
Это не GDPR-театр.
Цели
Для трекинга кликов использую data-metrika-goal на элементах. Метрика подхватывает их автоматически. Без кастомных скриптов на каждую кнопку.
ID Метрики передаётся через PUBLIC_YANDEX_METRIKA_ID - публичная build-time переменная в Astro. Если её нет в env - Метрика просто не встроится в сборку. Удобно для локальной разработки.
Деплой и инфраструктура
VPS + Nginx
Сайт лежит на самой дешёвой VPS. Статика через Nginx. Всё.
Почему не Vercel / Netlify / Cloudflare Pages? Потому что для статического сайта мне не нужна их инфраструктура. dist - это папка с HTML, CSS и JS. Nginx отдаёт её быстрее, чем я успеваю моргнуть. Полный контроль над конфигами, редиректами и заголовками.
SSL
Certbot с Let's Encrypt. Бесплатно, автообновление сертификатов. Настройка - 5 минут один раз.
Редиректы
# http -> https server { listen 80; return 301 https://$host$request\_uri; } # www -> apex server { server_name www.naderevo.com; return 301 https://naderevo.com$request\_uri; }
Плюс legacy-редиректы: когда я менял слаги старых постов, добавил 301-е на новые URL. Чтобы ссылки из поисковиков и чужих статей не вели в никуда.
Кэширование
Статические ассеты (JS, CSS, шрифты, картинки) отдаются с заголовком:
Cache-Control: public, max-age=31536000, immutable
Год кэша, immutable - браузер даже не пытается перепроверять. Astro генерирует файлы с хешами в именах, поэтому при новом деплое URL меняется и кэш инвалидируется автоматически.
Security headers
Базовый набор:
X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block Referrer-Policy: strict-origin-when-cross-origin
Не параноидальная конфигурация, но базовые вещи закрыты.
TypeScript
tsconfig наследует astro/tsconfigs/strict. Строгий режим. Alias @/* маппится на src/* - чтобы не писать ../../../components/.
{ "extends": "astro/tsconfigs/strict", "compilerOptions": { "paths": { "@/*": ["./src/*"] } } }
Для блога и лендинга строгий TypeScript может показаться overkill. Но когда у тебя двуязычные конфиги, Content Collections и куча переиспользуемых компонентов - он ловит ошибки, которые иначе всплыли бы в продакшне.
Конфигурация Astro
// astro.config.mjs export default defineConfig({ site: 'https://naderevo.com', // ... });
site - это не декорация. От него зависят canonical URL, sitemap, OG-теги. Без него половина SEO-разметки сломается или будет генерировать относительные пути вместо абсолютных.
Честные ограничения
Всё, что описано выше, работает. Но есть вещи, которые можно сделать лучше.
Корневой / не индексируется
Служебная redirect-страница на / - SEO-компромисс. Поисковик видит noindex и не считает её за контентную. Весь SEO-вес идёт на /ru/ и /en/. Это осознанное решение, но оно означает, что при заходе на голый домен пользователь видит не контент, а редирект.
OG-картинки - универсальные
Сейчас у сайта одна OG-картинка на все страницы. Для CTR в соцсетях уникальная картинка на каждую статью работает заметно лучше. Это в планах, но пока не сделано.
Webvisor и clickmap
Яндекс.Метрика умеет записывать сессии (Webvisor) и строить карту кликов. Я это не включаю - оно тяжелее для клиента.
Итого
Весь сайт - это:
Astro 6 собирает статику
Markdown-файлы для контента
TypeScript в strict-режиме
Nginx отдаёт
distCertbot для SSL
Яндекс.Метрика с cookie-consent
SEO-разметка: canonical, hreflang, JSON-LD, sitemap
VPS, Nginx, статические файлы. Это работало 10 лет назад, работает сейчас и будет работать через 10 лет.
Поддержка
Если хотите поддержать автора - подписывайтесь на мой Telegram-канал "На Дерево".
