Задача: сделать технический аудит сайта так, чтобы результат не зависел от того, кто его делал, в каком настроении и что успел вспомнить. И чтобы через полгода можно было открыть отчёт и сразу понять, что уже починили, а что нет.
Проблема почти всех аудитов, которые я видел, в одном: они состоят из мнений, а не из замеров. «Много запросов к базе», «страница грузится медленно», «есть проблемы с безопасностью». С тем же успехом можно написать «сайт какой-то не такой».
И каждый раз руками проходишь один и тот же путь: вспоминаешь, где смотреть версию ядра, как посчитать SELECT на страницу, какие служебные пути дёргать. Через месяц между проектами половина забывается, и начинаешь заново.
Так появился скилл. Скилл это папка с инструкциями и скриптами, которую подключают к ИИ-агенту. Дальше агент по этим инструкциям сам ходит по серверу, сам запускает команды, сам складывает находки в отчёт. То есть это не «нейросеть, которая глянет на сайт и что-то скажет», а методика с фиксированными шагами и требованием доказательств, которую агент исполняет шаг за шагом.
Если коротко: инструкция моя, а руки агентские. И это важная разница, к ней я ещё вернусь в конце.
Принцип технического аудита: замер, а не мнение
Вся конструкция держится на одном требовании. Каждая находка подтверждается либо замером (команда и её вывод: число запросов, время ответа, HTTP-код, размер), либо цитатой кода с файлом и строкой.
«Много запросов» это ещё не находка. Находка это «сорок семь SELECT на страницу там, где хватает пяти». Первое проверить нельзя, второе можно воспроизвести и оспорить.
Аудит, который не может предъявить команду и её вывод, это не аудит, а литература.
И второе правило, которое на практике оказалось самым полезным: чего проверить не смог, пиши в раздел «Что я НЕ проверил» с причиной. Молчание не значит «всё хорошо». Отчёт с честно перечисленными дырками в покрытии полезнее отчёта, где всё зелёное только потому, что половину никто не смотрел.
Что входит в технический аудит сайта
Методика разбита на блоки и идёт в фиксированном порядке. Порядок не случайный: сначала то, что может стоить денег, потом всё остальное. Блоков семь.
- Разведка. Стек, версия CMS, где код, есть ли git и какой в нём HEAD. HEAD фиксируется в шапке отчёта: это доказательство, что отчёт про это состояние кода, а не про вчерашнее.
- Безопасность. Служебные пути, AJAX-эндпоинты, заголовки, куки, доступ к админке. Идёт первым, потому что эти находки важнее всех остальных вместе.
- Производительность. Запросы к базе на страницу, TTFB, сжатие, кеш. Запросы считаются дельтой счётчика
Com_selectдо и после, и обязательно дважды, потому что если цифра не падает на втором прогоне, значит кеша нет. - Архитектура. Форки ядра, модули не на своём месте, дубли шаблонов.
- Данные. Индексы, размеры таблиц, EXPLAIN на типовых выборках.
- SEO и разметка. Мета-теги, карта сайта, дубли URL, редиректы.
- Код. Инъекции, экранирование, работа с персональными данными.
Это и есть «полный спектр»: не только скорость и не только SEO, а всё сразу, потому что проблемы обычно связаны. Медленный сайт может оказаться не медленным, а открытым. Как вышло в этот раз.
Набор при этом не привязан к одному движку. Механика одинаковая везде: посмотреть стек, пройтись по служебным путям, замерить отклик, проверить данные, прочитать код. Меняются только команды: на Битриксе одни, на WordPress другие, на самописном проекте третьи. Поэтому прогнать можно любой сайт, а не только любимую CMS.
При этом глубже всего скилл проработан для 1С-Битрикс. Не потому что он «только для Битрикса», а потому что там я знаю, где смотреть: какие опции отвечают за композит, какие папки трогает обновление ядра, куда платформа по умолчанию складывает бэкапы. На незнакомом движке часть проверок придётся уточнять по ходу. Это честнее, чем делать вид, что одна инструкция универсальна.
На выходе ровно один файл AUDIT.md. Никаких смет и часов: это уже разговор про деньги, а не про технику.
Технический аудит на живом сайте: bxstore.ru
Проверять методику на чужом проекте интересно, но непоказательно: там можно найти что-то одно и успокоиться. Поэтому первый полноценный прогон я сделал на своём.
bxstore.ru это интернет-магазин бытовой техники на 1С-Битрикс, около пятисот товаров, PHP 8.5, MySQL 8.0, nginx плюс Apache как бэкенд. Тот самый магазин, который у меня собран как готовое решение: каталог, поиск с опечатками, фильтр, корзина, личный кабинет, оформление заказа. Логично, что если продаёшь готовый магазин, то и проверять его надо соответственно. Иначе что продаёшь.
Один проход, семнадцать находок разного веса. Покажу самые показательные.
Кеш работает не там, где кажется
Первое, что обычно проверяют: включён ли кеш. Смотришь в настройки, видишь галочки, успокаиваешься. Я посчитал реальные запросы к базе:
Q="SHOW GLOBAL STATUS LIKE 'Com_select'"
a=$(mysql -N -e "$Q" | awk '{print $2}')
curl -s -o /dev/null https://bxstore.ru/catalog/
b=$(mysql -N -e "$Q" | awk '{print $2}')
echo "SELECT на страницу: $((b-a))"
Вышло от девяти до восемнадцати запросов на страницу. Для магазина это мало, норма двадцать–шестьдесят, значит кеш компонентов реально работает. Но второй прогон дал ту же цифру, а TTFB держался на 0.65–0.93 секунды.
При девяти запросах к базе такая задержка значит одно: время уходит не в MySQL, а в сборку HTML на PHP. Дальше вскрылось почему: в настройках стояло ~param_composite = N, композитный кеш выключен. Каждая страница собирается заново целиком, хотя отдельные компоненты при этом кешируются.
Типичная картина: половина кеша включена, половина нет, и никто не смотрел, как это выглядит вместе. Стабильная цифра на втором прогоне плюс высокий TTFB дают два факта, из которых сразу видно, куда копать. Без замеров осталось бы «вроде работает медленно».
Модуль не на своём месте
Модуль сжатия картинок лежал в папке ядра (bitrix/modules/), а не в local/modules/. Работал, WebP отдавался, всё хорошо. Пока не придёт обновление ядра.
ls bitrix/modules/ | grep -v -E "^(main|iblock|catalog|sale|...)$"
dev2fun.imagecompress
Папка ядра обновляется через систему обновлений CMS. Обновление может перезаписать сторонний модуль, и сайт останется без сжатия картинок, молча. Плюс при переносе на другой сервер папку ядра не копируют, так что модуль потеряется в дороге. А в git его нет, потому что bitrix/ не версионируется. И правильно: ядро не версионируют. Восстанавливать пришлось бы из бэкапа.
Главная находка: то, что не искал
Интереснее всего в аудитах то, чего не было в чеклисте. Тут это случилось на шаге проверки служебных путей.
Внутри веб-корня лежал каталог с полными резервными копиями. Так устроен Битрикс: бэкапы по умолчанию складываются в bitrix/backup/, то есть в папку, которую раздаёт веб-сервер.
curl -s -I "bxstore.ru/bitrix/backup/bxstore_20261109_full_xxxx.tar.gz"
HTTP/2 200
content-type: application/force-download
content-length: 485662011
Файл отдавался кому угодно, без авторизации, с поддержкой Range-запросов, то есть качать можно кусками, не привлекая внимания гигантским таймаутом в логах. Внутри архива SQL-дамп базы. А в дампе всё: заказы, клиенты, телефоны, адреса, хеши паролей, настройки интеграций с ключами.
Самое неприятное в том, что имя файла предсказуемое: домен_дата_время_full_восемь_символов. Эти восемь символов и есть единственная защита. Не пароль и не авторизация, а надежда на то, что никто не угадает.
Утешает одно: нашлось это на моём же сайте, до того как по нему прошёлся кто-то другой.
Почему одного правила в nginx недостаточно
Есть момент, на котором легко ошибиться. Самое очевидное решение, добавить в nginx:
location ^~ /bitrix/backup/ {
deny all;
return 404;
}
Оно не сработает. На типовой конфигурации Битрикса nginx файлы не отдаёт, он проксирует всё в Apache. Запрос уходит дальше, Apache видит файл и спокойно возвращает его. Проверяется просто: если после правки nginx «не применилось», значит отвечает другой сервер.
Закрывать надо на обоих уровнях. В nginx своим location, в Apache блоком Directory рядом с теми, что уже закрывают кеш:
<Directory /home/bitrix/ext_www/bxstore.ru/bitrix/backup>
AllowOverride none
Require all denied
</Directory>
И в nginx оставить вторым слоем, на случай, если настройки Apache перезапишет система управления конфигами. У меня vhost помечен «Ansible managed», так что следующая раскатка вернула бы всё как было.
Проверять после правки надо снаружи, а не изнутри сервера. И сразу же прогонять главную, каталог, поиск, страницу политики, картинки и админку. Закрыть доступ легко. Закрыть так, чтобы при этом ничего не отвалилось, уже сложнее.
Что ещё нашлось
Остальное короче, но это типовой набор для любого проекта, который давно живёт:
- HTTP без редиректа на HTTPS. Сайт отдаёт 200 по
http://, а не 301. При этом куки сессии идут без флагаSecure: летят по открытому каналу. - Нет HSTS и CSP. Браузер не знает, что на HTTPS надо ходить всегда, а любая инъекция скрипта исполняется без ограничений.
- Нет лимитов на авторизацию.
/bitrix/admin/index.phpможно дёргать тысячи раз в минуту. На соседнем сайте этого же сервера лимиты настроены: подход известен, просто не применён. - Карта сайта неполная. Отдельная карта с акционными страницами существует и отдаёт 200, но в индекс не включена. Акции обычно самые ценные страницы для поиска.
- Заглушка в H1. На главной магазина в H1 стояло «Сайт на 1С-Битрикс. Готовое решение для быстрого запуска». Осталось от шаблона. Первая буква, что характерно, латинская.
- Мусор от деплоя. Девять файлов
.DS_Storeв проде, залитых из PhpStorm. Опасности нет, но видно, что деплой копирует лишнее.
Что было дальше
Аудит без последствий остаётся просто документом, который через месяц никто не откроет. Поэтому дальше каждый пункт закрывается по порядку, от опасного к косметическому.
С бэкапами уже разобрался. Доступ к каталогу перекрыт на двух уровнях и проверен снаружи: файл отдаёт 403 вместо 200 и вместо 206 на частичный запрос. Дальше по списку: вынести сами архивы из веб-корня, включить композит, перенести модуль в local/modules/, добавить редирект на HTTPS и HSTS, поставить лимиты на авторизацию, дописать карту сайта, вычистить мусор от деплоя.
Каждый пункт означает правку в конфиге или в коде и повторный прогон того же замера. Не «наверное, теперь лучше», а конкретно: код ответа, число запросов, время отклика. Скучно, зато закрывается всё, а не то, что вспомнили.
Кстати, это хороший тест на то, что методика не врёт: если после исправлений повторный прогон не показывает тех же пунктов, значит замеры были настоящие, а не «на глаз».
Сейчас bxstore.ru можно потрогать руками: пройти путь покупателя от поиска до оформления заказа, покрутить фильтр, проверить поиск с опечаткой. Если интересно, что это за решение и как оно собирается, у меня на сайте есть отдельная страница про готовый магазин, там всё подробнее.
Что это даёт
Семнадцать находок за один проход, каждая с командой и выводом. Две из них можно было не узнать никогда.
Но главное не в находках. Главное, что следующий прогон займёт столько же, а не «сколько получится». И что через полгода можно открыть отчёт и увидеть, что исправлено, а что нет, не полагаясь на память.
Отдельно про то, чего скилл не делает. Не чинит. Не пишет смету, пока не попросят. Не выдаёт «оценку 73 из 100»: такая цифра ничего не значит и существует только для презентации. Он показывает, где болит, и даёт команду, которой это проверяется.
Хороший аудит это не список проблем, а список проблем, каждую из которых можно воспроизвести.
Скилл, который не стоит на месте
Сейчас таких проектов около десятка: на них методика отработана целиком, от разведки до готового отчёта. Точной цифры нет, но порядок такой. Разные стеки, разные серверы, разные истории: от самописных магазинов до проектов, где код трогали пять человек и никто уже не помнит зачем.
Каждый прогон что-то добавляет. Это не «написал один раз и пользуюсь»: список проверок растёт от проекта к проекту, и растёт он не из головы, а из конкретных находок. Пункт про бэкапы в веб-корне появился именно так: однажды наткнулся, теперь он проверяется на каждом сайте, едва ли не первым делом. То же самое с модулями не в той папке, с форками ядра, с предсказуемыми именами файлов.
Обратная сторона тоже есть. Иногда проверка оказывается пустой: срабатывает везде одинаково и ничего не значит. Тогда она уезжает в справочник и перестаёт быть обязательной. Методика чистится так же, как наполняется, иначе через год это будет не аудит, а телефонный справочник.
Практический эффект простой: то, что на первом прогоне отнимало кучу времени, сейчас идёт по накатанной. А освободившееся внимание уходит туда, где в списке ещё пусто, то есть на поиск новых «бэкапов в веб-корне». Поэтому скилл и продолжает улучшаться: у него всегда есть куда расти, потому что сайты ломаются изобретательнее, чем составляются чеклисты.
Важное: агенту это поручать не стоит
Стоит сказать прямо, потому что тема модная, а последствия реальные. Скилл писался для ИИ-агента, и соблазн большой: дал агенту доступ, сказал «сделай аудит» и занялся своими делами. Так делать не надо.
Причина не в том, что агент глупый. Причина в том, что аудит это работа с живым продакшеном. Агент по инструкции будет дёргать служебные пути, читать b_option, поднимать дампы, запускать EXPLAIN на больших таблицах, ходить curl-ом по эндпоинтам. Каждое из этих действий само по себе безобидно, но вместе они означают: кто-то посторонний трогает боевой сервер и делает это по своему усмотрению. Одна опечатка в команде, и вместо SELECT уходит DELETE. Один неверно понятый шаг, и «проверка нагрузки» превращается в нагрузку.
Второе. Агент ошибается уверенно. Он выдаст красивый отчёт с цифрами, и отличить выдуманный замер от настоящего по виду невозможно. Именно поэтому в методике и появилось требование «каждая находка с командой и выводом»: это не украшение, а способ поймать агента на вранье. Но работает оно только тогда, когда есть кому проверить.
Поэтому правило простое. Скилл нужен как ускоритель, а не как замена головы. Запускать прогон может агент. Собирать сырые данные тоже он. Читать вывод, решать, что из этого настоящая находка, и тем более что-то исправлять должен человек. Как минимум до того момента, пока не убедишься, что на этом проекте агент не врёт.
Агент может пройти по чеклисту. Отвечать за выводы всё равно человеку.
Практически это выглядит так: агент работает на копии или на тестовом окружении, показывает команды до запуска, а всё, что меняет состояние (правки конфигов, запросы на запись), выполняется только с подтверждения. Если окружение боевое и подтверждения нет, то и доступа к нему у агента быть не должно.
И третье, самое неочевидное. Аудит не сводится к «прогнать и забыть». Между «нашёл» и «исправил» лежит решение: что чинить сейчас, что можно отложить, что вообще не проблема для этого проекта. Агент так не умеет, у него нет контекста бизнеса, сроков и того, что заказчик переживёт, а что нет. Это не тот вывод, который стоит делегировать.
Забрать скилл
Архив с текущей версией лежит здесь: скачать скилл site-audit. Внутри инструкция по методике, справочники по проверкам и безопасности, скрипты для быстрой проверки и конвертеры отчёта.
Скилл написан под ИИ-агентов, которые умеют запускать команды и читать файлы. Если твой так не умеет, пользы не будет: это не плагин к чату, а инструкция для того, у кого есть руки.
Одно предупреждение, важное. Эта страница не обновляется автоматически, поэтому версия в архиве может отставать от той, что у меня в работе. Скилл живой, я его правлю после каждого проекта: то, что скачаешь сегодня, и то, что скачаешь через полгода, будут разными файлами. Актуализировать архив буду по возможности; если увидишь, что он старый, учти: сама методика в тексте выше живёт дольше любых скриптов.