П/ВИН

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

·9 мин чтения
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 настройка включала:

  1. Создание приложения в Dokploy с типом сборки Dockerfile
  2. Привязку к GitHub-репозиторию через custom git URL
  3. Добавление домена feberra.com с автоматическим SSL
  4. Настройку 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)
  • Нет canonical URL на служебных страницах
  • Нет security headers
  • X-Powered-By: Next.js в ответах (нежелательно)
  • Нет manifest.webmanifest для PWA
  • OG-изображение не генерируется динамически
  • Блог: нет BlogPosting schema, нет 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.

И наконец: параллельный подход к задачам — запускать несколько агентов одновременно на разные чанки — работает и экономит время, но требует координации. Часть агентов может упасть по техническим причинам, и это нужно предусматривать: проверять результаты каждого и добивать вручную то, что не применилось.

Ссылки и документация

Паша Вин
Паша Вин

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

Связанный проект

Feberra