Почему сайты умирают тихо: три истории из нашей практики
Меня зовут Евгений Семёнов, я технический директор ITfresh. Мы занимаемся IT-аутсорсингом для московских компаний до пятидесяти рабочих мест, держим собственные серверы в дата-центре МТС, и сайт клиента для нас — не «сделали и забыли», а зона ответственности по SLA. За пятнадцать с лишним лет я усвоил простую вещь: сайты почти никогда не умирают громко. Они умирают тихо — и владелец узнаёт об этом последним, обычно от клиента, который «не смог оставить заявку».
Три истории, с которых начиналось наше сотрудничество с тремя разными компаниями. Все сайты, что характерно, были «беспроблемные» — годами работали без единого обращения к подрядчику.
История первая: протухший сертификат. Производственная компания, сайт приносил пять—семь заявок в неделю. В какой-то момент заявки кончились. Через одиннадцать дней (!) владелец сам зашёл на сайт с телефона и увидел красный экран «Подключение не защищено». Сертификат Let’s Encrypt не продлился: автопродление сломалось ещё за три месяца до этого, а два предыдущих раза сертификат перевыпускали руками и забыли. Одиннадцать дней простоя лидогенерации — из-за задачи, которая решается одной строкой в cron и одной внешней проверкой.
История вторая: диск, съеденный логами. Интернет-каталог на VPS. PHP-ошибка в стороннем сниппете писала в лог по несколько тысяч строк в час. Через полгода лог занял весь диск, база данных не смогла записать очередную транзакцию, сайт начал отдавать 500 — причём не всегда, а «через раз», что делало диагностику по телефону особенно весёлой. Никто не следил ни за местом на диске, ни за самим логом, в котором проблема была видна за месяцы до падения.
История третья: взломанный сосед по shared-хостингу. Сайт визитка на дешёвом тарифе. Сам сайт никто не ломал — сломали соседний аккаунт на том же сервере, а хостер изолировал площадку целиком и разослал уведомление на почтовый ящик, заведённый десять лет назад и давно не читаемый. Сайт лежал, бэкапов у хостера «в рамках тарифа не предусмотрено», у владельца — тем более.
Заметьте: ни в одной истории не виновата CMS. Все три сайта могли работать на чём угодно. Отсюда главный тезис статьи: регламент эксплуатации нужен даже самой беспроблемной системе. MODX Revolution мы любим ровно за то, что этого регламента нужно мало — на порядок меньше, чем сайту на WordPress с двадцатью пятью плагинами. Но «мало» не значит «нисколько». Ниже — полный производственный регламент, по которому мы обслуживаем клиентские сайты на MODX: бэкапы, обновления, харденинг, мониторинг и действия при инцидентах. Забирайте и сверяйте со своим подрядчиком.
Бэкапы: наша схема 3-2-1 для сайтов на MODX
Правило 3-2-1 старо как мир, но продолжает спасать бизнесы: три копии данных, на двух разных носителях, одна — вне основной площадки. Для сайта это означает: рабочие данные на проде, локальный архив на том же сервере (быстрое восстановление) и офсайт-копия на независимом сервере (страховка от гибели площадки целиком — см. историю номер три).
Что бэкапим, а что можно не тащить
Сайт на MODX — это две сущности: файлы и база данных. В файлах обязательны каталог core/ (включая конфиг с реквизитами подключения), каталог assets/ с загруженными картинками и документами, кастомные шаблоны и корневые файлы. А вот core/cache/ в архив тащить не нужно: это генерируемый кэш, который MODX пересоберёт сам, зато весит он порой больше самого сайта и раздувает архивы впустую. База данных — только через mysqldump: копирование файлов СУБД «на живую» даёт неконсистентный дамп, из которого потом ничего не поднимется.
Расписание: cron делает всё сам
Наш типовой набор заданий на VPS клиента выглядит так:
# /etc/cron.d/site-backup
# ежедневный дамп БД в 02:10, храним 14 суток
10 2 * * * www-data mysqldump --single-transaction --quick \
--routines --triggers modx_db | gzip > /var/backups/site/db/modx-$(date +\%F).sql.gz
25 2 * * * www-data find /var/backups/site/db -name "*.sql.gz" -mtime +14 -delete
# еженедельный полный архив файлов (воскресенье 03:00), кэш исключаем
0 3 * * 0 www-data tar --exclude='core/cache' -czf \
/var/backups/site/files/site-$(date +\%F).tar.gz /var/www/site
# офсайт-копия дампов и архивов на независимый сервер (воскресенье 04:30)
30 4 * * 0 www-data rsync -a --delete /var/backups/site/ backup@offsite.example:/backups/client-site/
Ключ --single-transaction позволяет снимать дамп InnoDB-таблиц без блокировки сайта — посетители ночного дампа даже не заметят. Офсайт-приёмник у нас — отдельный сервер в другом дата-центре, доступ к которому есть только по ключу и только на запись в свой каталог: если прод скомпрометируют, дотянуться до копий с него нельзя.
Сводное расписание по правилу 3-2-1:
| Что | Как часто | Куда | Хранение |
|---|---|---|---|
| Дамп БД (mysqldump, gzip) | ежедневно, 02:10 | прод-сервер, /var/backups | 14 суток |
| Полный архив файлов без кэша | еженедельно, вс 03:00 | прод-сервер, /var/backups | 4 недели |
| Офсайт-копия (дампы + архивы) | еженедельно, вс 04:30 | независимый сервер в другом ЦОД | 8 недель |
| Тест восстановления на стейджинге | ежеквартально | стейджинг-площадка | протокол с замером времени |
Главное — тест восстановления
Непроверенный бэкап — это не бэкап, а надежда. Классика жанра: архивы исправно копятся два года, а внутри — дампы пустой базы, потому что после переноса сайта имя БД поменялось, а cron-задание — нет. Поэтому раз в квартал мы берём последний архив и последний дамп, разворачиваем их на стейджинге с нуля и засекаем время до полностью рабочего сайта. Результат — строчка в протоколе: дата, версия, время восстановления. Именно эта цифра, а не «у нас есть бэкапы», ложится в основу SLA: мы обещаем клиенту время восстановления, которое сами регулярно измеряем.
Обновления ядра и компонентов без страха
Вторая по частоте причина «мёртвых» сайтов после отсутствия бэкапов — страх обновлений. Подрядчик однажды обновил, что-то отвалилось, с тех пор сайт заморожен на версии пятилетней давности со всеми её дырами. Лечится это не смелостью, а процессом.
Минорные обновления 3.x: бэкап → стейджинг → прод
Актуальная ветка сегодня — MODX Revolution 3.2, вышедшая 17 февраля 2026 года и работающая на PHP 8.1–8.5. Минорные обновления внутри ветки 3.x ставятся прямо из менеджера пакетов — это штатная, обкатанная процедура. Ветка 2.8.x закончилась версией 2.8.7 и больше не поддерживается: если ваш сайт всё ещё на «двойке», это не «обновление», а отдельный проект миграции, и тянуть с ним не стоит — исправлений безопасности для 2.x больше не будет.
Наш порядок для любого обновления ядра неизменен: свежий бэкап (внеочередной, а не вчерашний ночной) → накатываем на стейджинг-копию → прокликиваем критические сценарии: главная, карточки, формы, авторизация в менеджере → только затем прод, в окно минимального трафика. Звучит бюрократично, занимает на практике меньше часа — и полностью снимает страх кнопки «Обновить».
Компоненты: почему в MODX это на порядок спокойнее, чем в WP
Дополнения для MODX живут в официальном репозитории extras.modx.com и в русскоязычном modstore.pro; обновляются они через тот же менеджер пакетов. Принципиальная разница с WordPress — в количестве. На типовом клиентском сайте у нас стоит шесть—восемь компонентов: вывод ресурсов, формы, галерея, SEO-набор, может быть, магазинный модуль. На типовом сайте WP — двадцать пять плагинов и больше, каждый со своим циклом релизов, своими конфликтами и своей историей уязвимостей. Обновить восемь пакетов из одного источника и обновить двадцать пять из разных — это разные вселенные по трудозатратам и рискам. Именно поэтому сопровождение MODX объективно дешевле: меньше движущихся частей.
Обновление PHP на хостинге: с 8.1 до 8.5 без сюрпризов
Раз в год-полтора приходит время поднимать версию PHP. Здесь тот же конвейер: на стейджинге переключаем версию, включаем вывод deprecated-предупреждений в лог, прогоняем сайт и смотрим, что пишут сниппеты и компоненты. Чаще всего чинить приходится не MODX (ядро 3.2 совместимо с 8.1–8.5 из коробки), а самописные сниппеты десятилетней давности. Только после чистого прогона переключаем версию на проде — и сутки наблюдаем за логом ошибок пристальнее обычного.
Харденинг MODX: что мы делаем на каждом сайте
У MODX хорошая репутация по безопасности — во многом потому, что нет зоопарка из десятков плагинов сомнительного происхождения. Но «безопаснее WordPress» не значит «неуязвим». Вот наш обязательный минимум для каждого сайта на сопровождении.
Переносим менеджер и закрываем его по IP
Адрес /manager знают все сканеры интернета — боты долбятся в него круглосуточно на любом сайте. Первым делом переносим админку на нестандартный путь (это штатная настройка при установке или правка конфига), а затем на уровне nginx разрешаем доступ только с известных адресов. Если у клиента нет статического IP — вешаем базовую HTTP-аутентификацию: второй пароль перед формой логина отсекает автоматизированный перебор полностью.
Запрещаем исполнение PHP там, где его быть не должно
Каталог assets/ — это загружаемые файлы: картинки, документы, архивы. Исполняемого кода там быть не должно никогда. Если злоумышленник каким-то образом протащит туда PHP-шелл — через дырявую форму загрузки, украденный пароль редактора, что угодно — этот блок превращает его находку в бесполезный текстовый файл:
# nginx: PHP в каталогах загрузок не исполняется никогда
location ~* ^/assets/.*\.php$ {
return 403;
}
# менеджер (перенесён с /manager) — только с доверенных адресов
location /panel/ {
allow 203.0.113.10; # офис клиента
allow 198.51.100.7; # площадка сопровождения ITfresh
deny all;
}
Права на файлы стандартные и скучные: каталоги 755, файлы 644, конфиг — 400, владелец — пользователь сайта, а не root. Скучное здесь и должно быть скучным.
Учётки: никакого общего admin
У каждого человека, который заходит в менеджер, — своя учётная запись со своей ролью. Общий логин admin на троих — это невозможность понять, кто что сделал, и невозможность безболезненно отключить уволившегося сотрудника. Пароли — только длинные и из менеджера паролей. Раз в квартал проходим по списку пользователей и отключаем тех, кто больше не работает с сайтом: по нашему опыту аудитов, «мёртвые» учётки с правами администратора находятся на двух сайтах из трёх.
fail2ban против перебора
Поверх ограничений по IP ставим fail2ban: он читает access-лог nginx и банит адреса, которые слишком настойчиво отправляют POST-запросы на форму логина менеджера. IP в логе ошибок MODX по умолчанию нет, поэтому честнее и надёжнее считать попытки именно по access-логу веб-сервера:
# /etc/fail2ban/filter.d/modx-manager.conf
[Definition]
failregex = ^<HOST> .* "POST /panel/ HTTP/.*"
# /etc/fail2ban/jail.d/modx.conf
[modx-manager]
enabled = true
port = http,https
filter = modx-manager
logpath = /var/log/nginx/site-access.log
maxretry = 6
findtime = 10m
bantime = 12h
Шесть POST-запросов на логин за десять минут от одного адреса — это либо человек, забывший пароль (разбанится через полсуток или напишет нам), либо перебор, которому там и место. Сюда же добавляем набор WAF-правил на уровне nginx: режем очевидные попытки инъекций в параметрах, обращения к служебным файлам, юзер-агенты известных сканеров. Это не полноценный WAF, но 90% автоматического шума он снимает бесплатно.
TLS, домены и почта сайта
Сертификат: автопродление, за которым всё равно следят
Все клиентские сайты у нас на бесплатных сертификатах Let’s Encrypt с автопродлением через certbot. Здесь живёт грабля из истории номер один: если на сервере nginx уже слушает 80-й порт, то certbot в режиме standalone не сможет поднять свой временный веб-сервер — и продление будет молча падать. Правильно — использовать nginx-плагин certbot или webroot-режим, когда проверочный файл кладётся в каталог, который уже раздаёт работающий nginx. И второй слой защиты: внешний мониторинг срока сертификата (об этом ниже), потому что «автоматика настроена» и «автоматика работает» — разные утверждения, что нам регулярно доказывает практика.
Про домен скажу одной строкой, потому что это самая обидная потеря: продление домена — календарное событие с напоминанием за месяц, а не письмо от регистратора на заброшенный ящик. Сайт с идеальными бэкапами бесполезен, если домен уехал к перекупщику.
SPF, DKIM, DMARC: чтобы заявки долетали
Форма обратной связи отправляет письма — и если у домена не настроены SPF, DKIM и DMARC, эти письма прилетают в спам или не прилетают вовсе. Владелец при этом уверен, что «заявок просто нет». Мы проверяем эти три записи на каждом сайте, который берём на сопровождение, и в половине случаев находим проблему: SPF отсутствует или не включает IP сервера, DKIM-подписи нет, DMARC не заведён. Настройка занимает час, а эффект — заявки начинают доходить. Отдельно рекомендуем дублировать заявки вторым каналом (в Telegram или CRM): почта не должна быть единственной ниткой между клиентом и вашим отделом продаж.
Мониторинг: как узнать о проблеме раньше клиента
Весь предыдущий регламент бесполезен, если о падении сайта вы узнаёте от покупателя. Правило простое: о любой проблеме первым должен узнавать подрядчик, а не владелец, и тем более не его клиенты.
Внешние проверки
Минимальный набор, который мы вешаем на каждый сайт с независимой площадки (мониторить сервер с него самого — бессмысленно):
- Доступность: HTTP-запрос к главной каждую минуту, алерт при коде ответа не 200 или таймауте;
- Срок сертификата: алерт за 14 дней до истечения — если автопродление сломалось, есть две недели на починку без спешки;
- Размер и содержимое ответа: страница «отдалась», но весит 3 КБ вместо 80 или не содержит контрольной фразы из подвала — это дефейс или ошибка шаблона, код ответа при этом может быть честным 200;
- Место на диске и возраст последнего бэкапа: алерт при заполнении 80% и если свежего дампа нет больше 26 часов. Вторая проверка важнее, чем кажется: молча сломавшийся бэкап — бомба замедленного действия.
Скорость и кэш
Раз в месяц замеряем Core Web Vitals ключевых страниц и сравниваем с прошлым замером. Деградация скорости почти всегда имеет конкретную причину: раздутые несжатые картинки, залитые редактором, новый тяжёлый скрипт от маркетолога, распухшая таблица сессий в базе. Кэш MODX при штатной работе обслуживает себя сам; чистка кэша — операция после правок шаблонов, а не магический ритуал «на всякий случай» по расписанию.
Контроль целостности файлов
Простая, но результативная проверка: по расписанию ищем PHP-файлы там, где их не должно быть — в первую очередь в assets/ — и сравниваем список файлов ядра с эталоном. Появился новый исполняемый файл в каталоге загрузок — алерт и разбор немедленно, потому что легитимных сценариев для такого события не существует. Эта проверка из одной строки на find не раз ловила заражения раньше, чем они успевали развернуться во что-то серьёзное.
Типовые инциденты и наши действия по SLA
Инциденты случаются даже при идеальном регламенте — вопрос лишь в том, есть ли на каждый типовой сценарий отработанный план и измеренное время восстановления. Наша таблица из договоров сопровождения (время — от момента обнаружения, которое благодаря мониторингу измеряется минутами):
| Инцидент | Наши действия | Целевое время восстановления |
|---|---|---|
| Сайт отдаёт 500 после обновления | откат файлов и БД из свежего пред-обновленческого бэкапа, разбор причин уже на стейджинге | до 60 минут |
| Протух TLS-сертификат | ручной перевыпуск, затем починка автопродления и ретест | до 1 часа |
| Переполнение диска / отказ сервиса | чистка логов и кэша, перезапуск сервисов, лечение первопричины | до 2 часов |
| Дефейс / заражение | изоляция сайта, поиск точки входа по логам, восстановление из заведомо чистой копии, смена всех паролей | 4–8 часов |
| Гибель хостинга / площадки | развёртывание из офсайт-копии на резервном сервере, переключение DNS | 8–24 часа |
Два комментария к таблице. Первый: время отката из бэкапа — не оценка «на глаз», а результат ежеквартальных тестов восстановления, о которых я писал выше; мы обещаем то, что регулярно репетируем. Второй: при заражении категорически нельзя «просто удалить подозрительные файлы» и жить дальше — без найденной точки входа заражение вернётся через неделю. Восстанавливаемся из чистой копии, закрываем дыру, меняем пароли — только в таком порядке и только целиком.
Сколько это стоит — и чек-лист самопроверки из 10 пунктов
Весь описанный регламент для сайта на MODX укладывается в несколько часов работы в месяц: почти всё автоматизировано, руками выполняются обновления по конвейеру, ежеквартальный тест восстановления и разборы алертов. В тариф сопровождения у нас входит: бэкапы по схеме 3-2-1 с офсайт-копией, обновления ядра и компонентов через стейджинг, харденинг и его поддержание, мониторинг со всеми перечисленными проверками, гарантированное время реакции и восстановления по таблице выше — плюс банк часов на мелкие правки контента и вёрстки.
Почему это дешевле сопровождения WordPress — арифметика, а не маркетинг: шесть—восемь компонентов из одного репозитория против двадцати пяти с лишним плагинов из разных источников; на порядок меньше история уязвимостей и, соответственно, срочных внеплановых патчей; нет регулярных конфликтов «плагин обновился и уронил соседа», которые съедают часы диагностики. Меньше движущихся частей — меньше часов — ниже цена при более высокой надёжности.
И обещанный чек-лист. Десять вопросов, которые владельцу сайта стоит задать текущему подрядчику — или себе — уже завтра. На каждый вопрос ответ должен быть конкретным, с датами и цифрами, а не «да, конечно, всё настроено»:
- Когда был сделан последний бэкап сайта и где физически лежит его копия за пределами основного сервера?
- Когда последний раз бэкап разворачивали и сколько времени занял подъём рабочего сайта?
- Какая версия MODX стоит на сайте и поддерживается ли она (ветка 2.x — уже нет)?
- Когда последний раз обновлялись ядро и компоненты и проверялось ли обновление на копии сайта до прода?
- Доступен ли менеджер по адресу /manager с любого IP без ограничений?
- Запрещено ли исполнение PHP в каталоге загрузок assets/?
- У каждого сотрудника своя учётка в менеджере — или все ходят под общим admin, включая уволившихся?
- Кто и как узнает о падении сайта ночью в субботу — и через сколько минут?
- Продлевается ли TLS-сертификат автоматически и мониторится ли его срок независимо от автоматики?
- Настроены ли SPF, DKIM и DMARC — и доходят ли письма с формы заявки до почты, а не до папки «Спам»?
Оставить комментарий