Feberra Landing: миграция с Vercel и SEO-оптимизация

Есть такой сценарий, который рано или поздно случается с каждым проектом, рождённым в облаке: стартовал на Vercel через v0, быстро собрал MVP, запустил — и вроде всё хорошо. Но потом приходит понимание, что нужен контроль над инфраструктурой, единый пайплайн деплоя, свои правила игры. Именно так произошло с Feberra — AI-платформой для создания видео из текста. Лендинг жил на Vercel, домен уже был, но вся инфраструктура оставалась за бортом нашей стандартной схемы. Пришло время это исправить.
Что такое Feberra и откуда она взялась
Feberra — это сервис, который позволяет генерировать видео из текстовых описаний с помощью AI. Лендинг был создан через v0.dev — инструмент Vercel для быстрого прототипирования интерфейсов на Next.js. Репозиторий лежал на GitHub, домен feberra.com уже работал, но проект не был интегрирован в нашу инфраструктуру на базе Dokploy.
Задача формулировалась просто: клонировать репозиторий, адаптировать под наш пайплайн, задеплоить на VPS, а потом провести полноценную SEO-оптимизацию — потому что v0-проекты в этом плане выходят "голыми".
Шаг первый: клонирование и анализ
Первым делом — посмотреть, что вообще есть в репо. Структура оказалась стандартной для v0: Next.js 15 (app router), TypeScript, Tailwind CSS, несколько статических страниц и блог с данными в blog-data.ts. Зависимости чистые, лишнего почти нет — кроме @vercel/analytics, который нам совершенно не нужен вне Vercel.
Ключевые вещи, которых не хватало для деплоя на собственном сервере:
output: "standalone"в конфиге Next.js (без него Docker-образ будет весить гигабайты)- Dockerfile для сборки
- Конфигурация Dokploy
Dockerfile и next.config: делаем проект портабельным
Первым делом обновил next.config.mjs:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
poweredByHeader: false, // убираем X-Powered-By для безопасности
// ... остальные настройки
}
export default nextConfigПараметр output: 'standalone' — это ключевой момент. Он говорит Next.js собрать минимально необходимый набор файлов для запуска, без node_modules целиком. Итоговый Docker-образ получается компактным и подходящим для production.
Далее — Dockerfile по нашему стандартному multi-stage паттерну:
FROM node:20-alpine AS base
# Зависимости
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN npm ci
# Сборка
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Runtime
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
CMD ["node", "server.js"]Три стадии: установка зависимостей → сборка → финальный образ только с тем, что нужно для запуска. Никаких dev-зависимостей в production.
Одновременно удалил @vercel/analytics из layout.tsx и package.json — этот пакет предназначен для аналитики на платформе Vercel и бесполезен за её пределами.
Деплой через Dokploy
Dokploy — это self-hosted альтернатива Vercel/Railway, которую мы используем как единую платформу для всех проектов. Он работает поверх Docker и Traefik, умеет в автодеплой по webhook и управление SSL через Let's Encrypt.
Для feberra.com настройка включала:
- Создание приложения в Dokploy с типом сборки
Dockerfile - Привязку к GitHub-репозиторию через custom git URL
- Добавление домена
feberra.comс автоматическим SSL - Настройку GitHub webhook для автодеплоя при пуше в
main
После того как DNS был направлен на VPS (A-запись на IP нашего сервера), оставалось подождать несколько минут пока Traefik запросит сертификат у Let's Encrypt. Первый запрос через curl вернул ошибку SSL — это нормально, сертификат запрашивается при первом обращении. Через минуту сайт уже отвечал по HTTPS с редиректом с HTTP.
Проверка:
curl -I https://feberra.com
# HTTP/2 200
# content-type: text/html; charset=utf-8Миграция с Vercel заняла меньше часа. Теперь feberra.com живёт на нашей инфраструктуре с автодеплоем по пушу.
SEO-оптимизация: разбираем по слоям
v0-проекты генерируют вполне рабочий UI, но SEO там обычно минимальный — базовые meta-теги и всё. После деплоя мы провели полный аудит и зафиксировали список проблем.
Что нашли при аудите
- Нет
sitemap.xmlиrobots.txt - Нет structured data (JSON-LD schema.org)
- Нет
canonicalURL на служебных страницах - Нет security headers
X-Powered-By: Next.jsв ответах (нежелательно)- Нет
manifest.webmanifestдля PWA - OG-изображение не генерируется динамически
- Блог: нет
BlogPostingschema, нетH1, нетCollectionPage - Страницы тегов индексировались без
noindex
Звучит как много — но по факту это несколько файлов и компонентов.
Sitemap и robots
Next.js 13+ поддерживает автогенерацию sitemap через специальный файл app/sitemap.ts:
import { MetadataRoute } from 'next'
import { getAllPosts, getAllCategories } from '@/lib/blog-data'
export default function sitemap(): MetadataRoute.Sitemap {
const posts = getAllPosts()
const categories = getAllCategories()
const postUrls = posts.map((post) => ({
url: `https://feberra.com/blog/${post.slug}`,
lastModified: new Date(post.date),
changeFrequency: 'monthly' as const,
priority: 0.7,
}))
const categoryUrls = categories.map((cat) => ({
url: `https://feberra.com/blog/category/${cat.slug}`,
lastModified: new Date(),
changeFrequency: 'weekly' as const,
priority: 0.5,
}))
return [
{ url: 'https://feberra.com', lastModified: new Date(), priority: 1.0 },
{ url: 'https://feberra.com/blog', lastModified: new Date(), priority: 0.8 },
...postUrls,
...categoryUrls,
]
}Аналогично — app/robots.ts с явным указанием sitemap и блокировкой индексации API-роутов.
Security headers
В next.config.mjs добавили заголовки безопасности:
async headers() {
return [
{
source: '/(.*)',
headers: [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'SAMEORIGIN' },
{ key: 'X-XSS-Protection', value: '1; mode=block' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=()',
},
],
},
]
},
poweredByHeader: false,Это и безопасность, и небольшой плюс к доверию со стороны поисковиков.
Schema.org разметка
Для главной страницы добавили Organization и FAQPage JSON-LD. Для блога — BlogPosting на каждую статью и CollectionPage на листинг:
// app/blog/[slug]/page.tsx
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.title,
description: post.excerpt,
datePublished: post.date,
dateModified: post.updatedAt || post.date,
author: {
'@type': 'Organization',
name: 'Feberra',
url: 'https://feberra.com',
},
publisher: {
'@type': 'Organization',
name: 'Feberra',
logo: {
'@type': 'ImageObject',
url: 'https://feberra.com/logo.png',
},
},
mainEntityOfPage: {
'@type': 'WebPage',
'@id': `https://feberra.com/blog/${post.slug}`,
},
}Schema.org разметка не даёт прямого буста в ранжировании, но открывает возможность для rich snippets в выдаче — звёздочки, FAQ-блоки, хлебные крошки. Для AI-сервиса это важно: визуальное отличие в выдаче повышает CTR.
Canonical URLs и noindex
Страницы тегов (/blog/tag/[tag]) — это дублирующий контент с точки зрения поисковиков. Добавили noindex, nofollow через metadata:
export const metadata: Metadata = {
robots: {
index: false,
follow: false,
},
}Для privacy policy и terms of service — явные canonical URL, чтобы исключить дублирование через www или http.
Динамическое OG-изображение
Next.js позволяет генерировать OG-изображения программно через app/opengraph-image.tsx. Вместо статичного PNG — динамический роут, который рендерит SVG с нужными параметрами через @vercel/og (работает и вне Vercel):
import { ImageResponse } from 'next/og'
export const runtime = 'edge'
export const alt = 'Feberra — AI Video Generation'
export const size = { width: 1200, height: 630 }
export default function Image() {
return new ImageResponse(
<div
style={{
background: 'linear-gradient(135deg, #0f0f23 0%, #1a1a3e 100%)',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{/* контент изображения */}
</div>
)
}llms.txt — SEO для AI-поисковиков
Отдельная история — файл llms.txt. Это emerging-стандарт, аналог robots.txt, но для LLM-агентов и AI-поисковиков (Perplexity, ChatGPT Browse, Claude). Поскольку Feberra — AI-продукт, её потенциальная аудитория активно пользуется этими инструментами. Добавили роут /llms.txt с описанием продукта в формате, дружественном для LLM.
Параллельная работа субагентов
Интересный технический момент этого проекта — мы применили подход параллельных субагентов для SEO-оптимизации. Все задачи разбили на 5 чанков и запустили их одновременно:
- Чанк 1: Critical Technical (canonical, security headers, noindex)
- Чанк 2: On-Page SEO (title, BlogPosting schema, H1, CollectionPage)
- Чанк 3: GEO (llms.txt)
- Чанк 4: Content Quality (счётчики категорий, meta descriptions)
- Чанк 5: финальный аудит
Часть субагентов столкнулась с ограничениями разрешений и не смогла применить изменения напрямую — это нормальная ситуация при параллельном выполнении. Успешные изменения были закоммичены, а недостающие — доделаны вручную. Итог — все 12 задач выполнены, npm run build прошёл без ошибок, 44 страницы сгенерированы статически.
Результат
После всех изменений:
- feberra.com работает на HTTPS с автодеплоем по пушу в
main - Sitemap доступен по
/sitemap.xml, индексирует все страницы и категории блога robots.txtкорректно настроен- Security headers проходят проверку
- Schema.org разметка на главной, блоге и каждой статье
- OG-изображение генерируется динамически
- Страницы тегов исключены из индексации
llms.txtготов для AI-поисковиковX-Powered-Byубран из заголовков
Деплой занял около часа. SEO-оптимизация — ещё два часа в два захода. Итого полноценный production-ready лендинг с правильной инфраструктурой вместо Vercel-зависимого прототипа.
Выводы и уроки
Первый и главный вывод: v0 — отличный инструмент для быстрого старта, но не для production. Он генерирует работающий UI, но инфраструктурные вещи — Dockerfile, SEO, security headers — нужно добавлять руками. Это не недостаток v0, это просто разграничение ответственности: инструмент делает то, для чего он создан.
Второй вывод: output: "standalone" в Next.js — это не опция, это необходимость при деплое в Docker. Без него образ будет весить 2-3 GB, запускаться долго и жрать ресурсы. С ним — компактный, быстрый, предсказуемый.
Третий вывод касается SEO. Многие воспринимают поисковую оптимизацию как что-то про контент и ключевые слова. Но техническая SEO-база — canonical URLs, structured data, sitemap, правильные заголовки — это фундамент, без которого весь контент работает вполсилы. Добавить это к готовому Next.js-проекту занимает несколько часов, а эффект долгосрочный.
Четвёртый вывод: llms.txt — это ставка на будущее. Сейчас это нишевый стандарт, но AI-поисковики растут. Для AI-продукта вроде Feberra особенно важно быть правильно описанным в экосистеме LLM-агентов — потенциальные пользователи всё чаще находят инструменты через ChatGPT и Perplexity, а не через Google.
И наконец: параллельный подход к задачам — запускать несколько агентов одновременно на разные чанки — работает и экономит время, но требует координации. Часть агентов может упасть по техническим причинам, и это нужно предусматривать: проверять результаты каждого и добивать вручную то, что не применилось.
Ссылки и документация
- Next.js output: standalone — официальная документация по standalone режиму
- Next.js Metadata API — sitemap, robots, OG-изображения
- Schema.org BlogPosting — спецификация разметки для блог-постов
- Dokploy документация — self-hosted деплой-платформа
- llms.txt стандарт — описание формата для AI-агентов

AI-инженер, предприниматель, маркетолог. Основатель feberra.com и x10seo.ru. 13 лет в перфоманс-маркетинге, 3 года в системной интеграции AI в бизнес.
Связанный проект
Feberra →