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

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

rechecker

Оглавление

  1. Как всё началось: боль, знакомая каждому разработчику

  2. MVP: инструмент, который работал только для меня

  3. От инструмента — к сервису: что стало триггером

  4. Архитектура и технологии: как я всё это построил

  5. Основные модули

  6. API и интеграции

  7. Проблемы, с которыми столкнулся

  8. Техническое развитие

  9. Техническая архитектура монетизации

Как всё началось: боль, знакомая каждому разработчику

В какой-то момент я понял, что 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 до реализации стриминговых ответов.

Главный урок: даже простая идея «собрать всё в одном месте» может превратиться в серьёзный технический вызов, если подходить к реализации основательно.

Комментарии (1)


  1. Salamander174
    19.07.2025 15:06

    Ах как чётко просматривается работа v0 особенно видно по стеку и дизайну)

    Более того собирать все это самостоятельно не было ни какого смысла аналогичных и более развитых и что немало важно бесплатных инструментов на рынке просто тьма