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

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