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

Оглавление
-
Как всё началось: боль, знакомая каждому разработчику
-
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 до реализации стриминговых ответов.
Главный урок: даже простая идея «собрать всё в одном месте» может превратиться в серьёзный технический вызов, если подходить к реализации основательно.
ссылка на оригинал статьи https://habr.com/ru/articles/929154/
Добавить комментарий