У каждого веб-разработчика есть моменты, когда рутинные задачи съедают больше времени, чем сама разработка. Проверить редиректы, оптимизировать мета-теги, убедиться, что изображения в порядке, а заголовки везде прописаны — всё это нужно делать регулярно. И если ты ведёшь проекты с нуля и под ключ, то таких задач становится десятки.
Я устал от этой рутины и решил автоматизировать задачи, создав удобный инструмент для себя. Что из этого получилось и какие технические проблемы пришлось решать — расскажу в этой статье.

Оглавление
- Как всё началось: боль, знакомая каждому разработчику 
- MVP: инструмент, который работал только для меня 
- От инструмента — к сервису: что стало триггером 
- Архитектура и технологии: как я всё это построил 
- Основные модули 
- API и интеграции 
- Проблемы, с которыми столкнулся 
- Техническое развитие 
- Техническая архитектура монетизации 
Как всё началось: боль, знакомая каждому разработчику
В какой-то момент я понял, что 30–40% времени уходит на одно и то же. Типичные задачи:
- Проверить, работает ли редирект с http на https 
- Убедиться, что у всех страниц корректные мета-теги 
- Посмотреть, не забыли ли прописать ALT у изображений 
- Убедиться, что robots.txt не блокирует нужные страницы 
- Узнать, на какой CMS сделан сайт клиента или конкурента 
Я использовал десятки сервисов: где-то онлайн, где-то локально, где-то даже использовал Телеграм ботов. Список ссылок на утилиты постоянно рос. А ещё хуже — он расползся по заметкам, проектам, Google Docs и личным перепискам. Это стало неудобно.
Тогда я решил: а почему бы не собрать всё в одном месте?

MVP: инструмент, который работал только для меня
Я не думал делать что-то публичное. Первой целью было просто решить свои задачи. Открыл редактор кода Cursor, написал инструкции в Rules, подключил парочку MCP tools и понеслось.
Вообще я PHP-разработчик, работающий с WordPress и Битриксом, но здесь решил, что нужно выбрать какой-то современный стек — с такими мыслями остановился на Next.js. Кто будет ругать Вайбкодинг — успокойтесь. Я давно в разработке и понимаю, что пишет мне ИИ Cursor, поэтому использую его больше как ментора и помощника, а не во всём полагаюсь на него.
Так появился MVP. Это была страничка, в которую можно вставить URL, нажать кнопку — и получить:
- Цепочку редиректов 
- Все мета-теги (включая Open Graph и Twitter Cards) 
- Все изображения на странице с их весом, размерами и alt-текстами 
- Заголовки ответа сервера 
- Основные ошибки и рекомендации 
Работало просто. Стек был такой:
- Next.js 13 + App Router — для SSR и API 
- TypeScript — строгая типизация 
- TailwindCSS — базовая стилизация 
- Cheerio — парсинг HTML (версия 0.22.0 — проверенная временем) 
- Axios — сетевые запросы с кастомными заголовками 
- Whois — получение информации о доменах 
- XML2JS — парсинг sitemap и других XML 
- Sharp — обработка изображений 
Вот пример кода, который определяет цепочку редиректов:
const checkRedirects = async (url: string) => {
  const redirects = []
  let currentUrl = url
  let redirectCount = 0
  const maxRedirects = 10
  while (redirectCount < maxRedirects) {
    const response = await fetch(currentUrl, {
      method: 'HEAD',
      redirect: 'manual'
    })
    redirects.push({
      url: currentUrl,
      status: response.status,
      headers: Object.fromEntries(response.headers.entries())
    })
    if (response.status >= 300 && response.status < 400) {
      const location = response.headers.get('location')
      if (!location) break
      
      currentUrl = new URL(location, currentUrl).toString()
      redirectCount++
    } else {
      break
    }
  }
  return redirects
}

Или вот кусок, который вытаскивает мета-теги:
const extractMetaTags = (html: string) => {
  const $ = cheerio.load(html)
  
  return {
    title: $('title').text(),
    description: $('meta[name="description"]').attr('content'),
    ogTitle: $('meta[property="og:title"]').attr('content'),
    ogDescription: $('meta[property="og:description"]').attr('content'),
    twitterCard: $('meta[name="twitter:card"]').attr('content'),
    canonical: $('link[rel="canonical"]').attr('href'),
    hreflang: $('link[rel="alternate"][hreflang]').map((_, el) => ({
      hreflang: $(el).attr('hreflang'),
      href: $(el).attr('href')
    })).get(),
    schemas: $('script[type="application/ld+json"]').map((_, el) => {
      try {
        return JSON.parse($(el).html() || '')
      } catch {
        return null
      }
    }).get().filter(Boolean)
  }
}
Это не был продукт. Это был инструмент "для себя", но я начал пользоваться им постоянно.
От инструмента — к сервису: что стало триггером
Я начал кидать ссылку коллегам. Типа: «Зацени чо наВайбкодил». Ответ был один и тот же: «Удобно, пили проект дальше». Коллеги начали пользоваться. Потом друзья, потом друзья друзей. Тогда я понял — это можно развивать.
Я сел и подумал: а что, если развить это в полноценный инструмент?
Так появился веб-сервис с набором утилит, объединенных в один интерфейс.
Архитектура и технологии: как я всё это построил
Я понимал, что инструмент может вырасти. Поэтому сразу заложил масштабируемую архитектуру:
Основной стек:
- Frontend / Backend: Next.js 13 с App Router 
- Типизация: TypeScript 
- UI: TailwindCSS 
- БД: PostgreSQL 
- ORM: Prisma 
- Аутентификация: NextAuth.js + система API-ключей 
- Интеграции: OpenAI GPT-4 для AI-инструментов 
Также предусмотрел:
- Стриминговые ответы, чтобы не ждать полный анализ 
- Параллельную обработку URL 
- Кэширование результатов на уровне PostgreSQL 
Схема базы данных
Основные таблицы:
-- ПользователиCREATE TABLE users (  id SERIAL PRIMARY KEY,  username VARCHAR(50) UNIQUE,  email VARCHAR(255) UNIQUE,  role VARCHAR(20) DEFAULT 'user',  createdAt TIMESTAMP DEFAULT NOW());-- API ключи (базовая версия)CREATE TABLE api_keys (  id SERIAL PRIMARY KEY,  key VARCHAR(255) UNIQUE,  userId INTEGER REFERENCES users(id),  usageCount INTEGER DEFAULT 0,  lastUsedAt TIMESTAMP,  revoked BOOLEAN DEFAULT FALSE);-- Логи использованияCREATE TABLE logs (  id SERIAL PRIMARY KEY,  userId INTEGER REFERENCES users(id),  service VARCHAR(100),  url TEXT,  createdAt TIMESTAMP DEFAULT NOW());-- Будущая таблица для токеновCREATE TABLE user_tokens (  id SERIAL PRIMARY KEY,  userId INTEGER REFERENCES users(id),  tokensBalance INTEGER DEFAULT 0,  tokensUsed INTEGER DEFAULT 0,  lastRefill TIMESTAMP DEFAULT NOW());Основные модули
На данный момент реализовано 16 инструментов:
? SEO-инструменты
? Анализ редиректов
- Проверка 301/302 редиректов 
- Поиск цепочек и бесконечных перенаправлений 
- Заголовки на каждом шаге 
- Определение финального статуса и URL 
?️ Анализ мета-тегов
- Проверка длины title/description 
- Анализ OG и Twitter Cards 
- Поиск дублирующихся тегов 
- Подсказки для улучшения 
?️ Работа с изображениями
- Извлечение размеров и веса 
- Проверка alt и SEO-дружественных названий файлов 
- Подсказки по lazy loading 
? Анализ ключевых слов
- Плотность ключевых слов на странице 
- Распределение по заголовкам H1-H6 
- Рекомендации по оптимизации 
?️ Проверка sitemap
- Валидация XML-sitemap 
- Проверка доступности URL из карты сайта 
- Анализ структуры и ошибок 
? Техническая диагностика
// Пример комплексной проверки сайта
const techAnalysis = async (url: string) => {
  const domain = new URL(url).hostname
  
  const [
    sslInfo,
    dnsRecords,
    cdnInfo,
    performance,
    compression
  ] = await Promise.all([
    checkSSLCertificate(domain),
    checkDNSRecords(domain),
    detectCDN(domain),
    measurePerformance(url),
    checkCompression(url)
  ])
  return {
    ssl: {
      valid: sslInfo.valid,
      issuer: sslInfo.issuer,
      expiresAt: sslInfo.expiresAt,
      daysUntilExpiration: sslInfo.daysUntilExpiration
    },
    dns: dnsRecords,
    cdn: cdnInfo,
    performance: {
      loadTime: performance.loadTime,
      firstByte: performance.ttfb,
      httpVersion: performance.httpVersion
    },
    compression: compression.enabled
  }
}
- Проверка SSL, DNS, CDN 
- Анализ заголовков безопасности 
- Определение HTTP версии и кэширования 
- Проверка версий PHP 
⚙️ Определение CMS и технологий
Изначально планировал использовать Wappalyzer — популярную библиотеку для определения технологий. Но столкнулся с проблемами совместимости в Next.js. Поэтому создал собственную систему детекции через паттерны:
const cmsSignatures = {
  'WordPress': {
    patterns: [
      /wp-content\/themes/i,
      /wp-includes/i,
      /wp-json/i,
      /wp-admin/i,
      /wordpress/i
    ],
    metaTags: [
      { name: 'generator', content: /wordpress/i }
    ]
  },
  'Bitrix': {
    patterns: [
      /bitrix/i,
      /bx\.js/i,
      /\/bitrix\/templates/i,
      /BX\.(message|ajax)/i
    ]
  },
  'Tilda': {
    patterns: [
      /tilda\.js/i,
      /tilda\.css/i,
      /data-tilda/i,
      /tildacdn/i
    ]
  }
  // ... еще 20+ CMS и конструкторов
}
// Алгоритм определения с весами
const fallbackDetection = (html: string, headers: Record<string, any>) => {
  for (const [cms, config] of Object.entries(cmsSignatures)) {
    const matches = config.patterns.filter(pattern => pattern.test(html))
    if (matches.length > 0) {
      const confidence = Math.min((matches.length / config.patterns.length) * 100, 100)
      if (confidence > 30) {
        return { name: cms, confidence, detected_by: 'patterns' }
      }
    }
  }
}
Дополнительная детекция:
- Meta-теги - generatorи- X-Powered-Byзаголовки
- DNS записи (через whois пакет) 
- Технологии аналитики (Google Analytics, Яндекс.Метрика) 
- CDN провайдеры (Cloudflare, MaxCDN) 
- Фреймворки (React, Vue, Angular) 
? ИИ-инструменты
Интеграция с GPT-4 для:
// Рерайт текста
const rewriteText = async (text: string, style: string) => {
  const response = await openai.chat.completions.create({
    model: "gpt-4",
    messages: [
      {
        role: "system",
        content: `Перепиши текст в стиле: ${style}. Сохрани смысл, но измени формулировки.`
      },
      { role: "user", content: text }
    ],
    temperature: 0.7
  })
  
  return response.choices[0].message.content
}
- Рерайт текстов с разными стилями 
- Копирайтинг и генерация контента 
- Чат-помощник по SEO и аналитике 
?️ Инструменты разработчика
JSON Formatter — форматирование и валидация JSON
try {
  const parsed = JSON.parse(inputText)
  const formatted = JSON.stringify(parsed, null, 2)
  setResult({ valid: true, formatted })
} catch (error) {
  setResult({ valid: false, error: error.message })
}

PHP Редактор — онлайн исполнение PHP кода
SVG Спрайт генератор — использую библиотеку svg-sprite-generator:
import { createSprite } from 'svg-sprite-generator'
const generateSprite = async (svgFiles: File[]) => {
  const sprites = await Promise.all(
    svgFiles.map(file => file.text())
  )
  return createSprite(sprites)
}
SVG Encoder — конвертация в data URI для CSS
Конвертер эмодзи — HTML коды для верстки
Excel экспорт — через библиотеку xlsx для сохранения результатов
API и интеграции
Система API-ключей
Базовая система для внешнего доступа:
// Простая валидация API ключа
const validateApiKey = async (apiKey: string) => {
  const keyRecord = await prisma.apiKey.findUnique({
    where: { key: apiKey },
    include: { user: true }
  })
  if (!keyRecord || keyRecord.revoked) {
    return { valid: false, error: 'Invalid API key' }
  }
  // Обновляем статистику использования
  await prisma.apiKey.update({
    where: { id: keyRecord.id },
    data: {
      usageCount: { increment: 1 },
      lastUsedAt: new Date()
    }
  })
  return { valid: true, userId: keyRecord.userId }
}
Текущие возможности API:
- Базовая аутентификация по ключу 
- Отслеживание использования 
- Логирование запросов 
- CORS поддержка 
В планах:
- Токенная система оплаты 
- Ограничения по количеству запросов 
- Разные тарифы доступа 
И так далее, не буду здесь устраивать документацию.
Проблемы, с которыми столкнулся

1. Проблемы с Wappalyzer в Next.js
Проблема: Wappalyzer требует браузерное окружение и конфликтует с SSR.
Решение: Создал собственную систему детекции:
// Временно отключаем Wappalyzer
// const TechDetector = require('web-technology-detector')
// const detector = new TechDetector()
// const results = await detector.url(url)
// Используем fallback detection
const fallbackResult = fallbackDetection(html, headers, url)
2. Сайты блокируют ботов
Проблема: Многие сайты возвращают 403 или блокируют запросы от серверов.
Решение:
const headers = {
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'ru-RU,ru;q=0.9,en-US;q=0.8',
  'Accept-Encoding': 'gzip, deflate, br',
  'DNT': '1',
  'Connection': 'keep-alive'
}
3. Большой объём данных
Проблема: HTML страницы могут весить 5+ МБ, что замедляет обработку.
Решение:
const response = await axios.get(url, {
  maxContentLength: 10 * 1024 * 1024, // 10MB лимит
  timeout: 30000,
  responseType: 'stream'
})
// Обработка частями
let chunks = ''
response.data.on('data', (chunk: Buffer) => {
  chunks += chunk.toString()
  if (chunks.length > 2 * 1024 * 1024) { // 2MB достаточно для анализа
    response.data.destroy()
  }
})
4. Производительность
Проблема: Анализ может занимать 30+ секунд для больших сайтов.
Решение: Параллельная обработка и стриминг:
const analyzeWebsite = async (url: string) => {
  // Запускаем все проверки параллельно
  const [redirects, metaTags, images, tech] = await Promise.allSettled([
    checkRedirects(url),
    checkMetaTags(url),
    checkImages(url),
    checkTechnologies(url)
  ])
  // Отправляем результаты по мере готовности
  return {
    redirects: redirects.status === 'fulfilled' ? redirects.value : null,
    metaTags: metaTags.status === 'fulfilled' ? metaTags.value : null,
    images: images.status === 'fulfilled' ? images.value : null,
    technologies: tech.status === 'fulfilled' ? tech.value : null
  }
}
Техническое развитие
Проект продолжает развиваться. Основные направления работы:
Архитектурные улучшения
- Система ограничений — реализация токенной логики 
- Пользовательские дашборды — интерфейс управления аккаунтом 
Новые функции
- Массовый анализ URL — проверка сразу 100+ страниц 
- Telegram-бот для экспресс-проверок 
- Chrome-расширение для анализа прямо в браузере 
- Экспорт в PDF — генерация отчетов 
Производительность и масштабирование
- GraphQL API для гибких запросов 
- Real-time мониторинг изменений сайтов 
- Кэширование для ускорения повторных запросов 
- Очередь задач для тяжелых операций 
Техническая архитектура монетизации (планы)
Концепция токенной системы
Рассматриваю возможность внедрения системы токенов, где каждая операция будет "стоить" определенное количество условных единиц:
const serviceCosts = {
  'redirect-check': 1,        // 1 токен
  'meta-tags': 2,            // 2 токена  
  'tech-analysis': 5,        // 5 токенов
  'ai-rewrite': 10,          // 10 токенов
  'full-audit': 20           // 20 токенов
}
const processRequest = async (service: string, userId: number) => {
  const cost = serviceCosts[service]
  const user = await getUserTokens(userId)
  
  if (user.tokensBalance < cost) {
    throw new Error('Insufficient tokens')
  }
  
  await deductTokens(userId, cost)
  return await executeService(service)
}
Предварительные лимиты (концепция):
- Базовый: 100 токенов/мес 
- Расширенный: 500 токенов/мес 
- Профессиональный: 2000 токенов/мес 
Текущее состояние
На данный момент все функции доступны без ограничений. Это позволяет:
- Собрать пользовательский фидбек 
- Протестировать нагрузку на сервер 
- Проанализировать паттерны использования 
Техническая мотивация статьи
Хочется поделиться опытом разработки pet-проекта с технической стороны:
- Какие архитектурные решения принимал и почему 
- С какими проблемами столкнулся при выборе стека 
- Как решал вопросы производительности и масштабирования 
- Какие компромиссы приходилось делать 
Если интересна тема разработки подобных инструментов — готов обсуждать в комментариях.
Обратная связь
Если интересны технические детали реализации или есть вопросы по архитектуре — пишите в комментарии или в Telegram: t.me/dobryninoleg
Всегда рад обсудить опыт разработки подобных инструментов.
Заключение
Проект не изобретает ничего принципиально нового — он просто объединяет существующие инструменты проверки в один интерфейс. Но именно так, я считаю, и появляются по-настоящему полезные решения: когда начинаешь автоматизировать свою рутину, а потом понимаешь, что у коллег те же боли.
За несколько месяцев разработки я прошёл путь от простого скрипта до архитектуры, которая может масштабироваться. Самое интересное — каждая техническая проблема заставляла искать нестандартные решения, от создания собственной системы детекции CMS до реализации стриминговых ответов.
Главный урок: даже простая идея «собрать всё в одном месте» может превратиться в серьёзный технический вызов, если подходить к реализации основательно.
Комментарии (6)
 - bootbash19.07.2025 15:06- Чем-то напомнило это https://github.com/jaywcjlove/tools 
 Было бы интересно посмотреть ваш сервис онлайн
 - xronelx19.07.2025 15:06- Так этого добра бесплатно и впрямь хватает. А на том же codecanyon тьма готовых решений от индусов, с большим функционал ом. Смысл изобретать велосипед? 
 
           
 
Salamander174
Ах как чётко просматривается работа v0 особенно видно по стеку и дизайну)
Более того собирать все это самостоятельно не было ни какого смысла аналогичных и более развитых и что немало важно бесплатных инструментов на рынке просто тьма
Gothicserge
А можно ссылки на бесплатные и функциональные инструменты?
Pitfil
Gothicserge
Конечно же нет :))