«Сайт медленный» — это симптом, а не диагноз. Причина может быть в чём угодно: в тяжёлом запросе к базе, в упёршемся диске, в соседнем проекте на том же сервере или вообще в картинках на 8 мегабайт, которые контент-менеджер залил без сжатия.
Правильная диагностика идёт по порядку — от общего к частному. Так вы за двадцать минут находите узкое место вместо того, чтобы наугад менять настройки.
Шаг 0. Убедиться, что дело в сервере
Прежде чем лезть в консоль, отделите проблемы сервера от проблем фронтенда.
Замерьте время ответа сервера — это то, за сколько он отдаёт саму страницу, без картинок и скриптов:
curl -o /dev/null -s -w "Время ответа: %{time_total}s\n" https://example.com
Ориентиры:
| Время | Что это значит |
|---|---|
| меньше 0,3 с | с сервером всё в порядке, ищите в вёрстке и скриптах |
| 0,3–1 с | терпимо, но есть что улучшать |
| больше 1 с | проблема на сервере, продолжаем диагностику |
Если сервер отвечает за 0,2 секунды, а страница «грузится вечно» — дело не в нём. Смотрите вес изображений, количество подключаемых скриптов и внешние виджеты: чаты, аналитику, карты. Один чужой скрипт способен затормозить всю страницу.
Шаг 1. Посмотреть общую картину
Подключитесь к серверу и выполните:
uptime
free -h
df -h
Три команды дают три ответа.
Load average выше числа ядер (nproc) — не хватает процессора, запросы стоят в очереди.
Swap используется — кончилась память. Это самая частая причина внезапного замедления: система начинает гонять данные на диск, и всё становится медленнее в разы.
Диск заполнен больше чем на 90% — часть операций уже не проходит. При 100% сайт просто ляжет.
Дальше идём в ту сторону, где нашлась аномалия.
Шаг 2. Если не хватает процессора
Смотрим, кто именно грузит:
htop
Сортировка по CPU — клавиша F6 или щелчок по колонке. Дальше по тому, что увидели:
Грузит php-fpm — тяжёлый код или отсутствие кэша. Самый частый случай на WordPress и Bitrix.
Грузит mysqld — проблема в запросах к базе, переходите к шагу 4.
Грузит незнакомый процесс — разберитесь, что это. Бывает, что сервер майнит чужую криптовалюту после взлома через старый плагин. Проверьте:
ps aux --sort=-%cpu | head -10
Если процесс подозрительный — не спешите просто убивать его, сначала посмотрите, откуда он запущен:
ls -l /proc/PID/exe
Нагрузка от ботов. Посмотрите, кто ходит на сайт:
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Если один адрес сделал десятки тысяч запросов — это парсер или атака. Заблокировать:
ufw deny from 1.2.3.4
Отдельная тема, если запросов много и с разных адресов: Защита от DDoS.
Шаг 3. Если кончилась память
Найдите главных потребителей:
ps aux --sort=-%mem | head -10
Дело в PHP-FPM. Чаще всего процессов запущено больше, чем помещается в память. Посчитайте: сколько памяти занимает один процесс (обычно 50–120 МБ) и сколько их может быть запущено одновременно.
nano /etc/php/8.3/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
Значение pm.max_children подбирается так: свободная память, делённая на размер одного процесса. На 2 ГБ с учётом базы и системы это обычно 8–12.
Слишком большое значение приводит ровно к тому, от чего вы лечитесь: процессов много, память кончается, сервер уходит в своп.
Дело в MySQL. По умолчанию MySQL настроен довольно скромно, но на маленьких серверах и это много. Ключевой параметр:
nano /etc/mysql/mysql.conf.d/mysqld.cnf
innodb_buffer_pool_size = 512M
Ориентир — около половины доступной памяти, если база на том же сервере, что и сайт. Больше — не всегда лучше: памяти должно хватать и остальным.
systemctl restart mysql
Утечка в приложении. Если процесс растёт день ото дня и сбрасывается только при перезапуске — это утечка. Временное решение — перезапуск по расписанию, правильное — искать причину в коде.
Шаг 4. Если упёрлись в базу
База данных — самая частая причина медленных сайтов после отсутствия кэша.
Включите журнал медленных запросов:
nano /etc/mysql/mysql.conf.d/mysqld.cnf
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
systemctl restart mysql
Через день посмотрите, что накопилось:
tail -50 /var/log/mysql/slow.log
Или сразу сводку по самым тяжёлым:
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
Чаще всего в топе оказываются запросы без индексов. Проверить конкретный запрос:
EXPLAIN SELECT * FROM orders WHERE email = 'test@example.com';
Если в колонке type стоит ALL — база перебирает всю таблицу целиком. Индекс решает проблему мгновенно:
CREATE INDEX idx_email ON orders(email);
На таблице в сотни тысяч строк разница бывает в сотни раз.
Посмотреть, что происходит в базе прямо сейчас:
SHOW FULL PROCESSLIST;
Запросы, висящие по несколько секунд, видно сразу.
Шаг 5. Если упёрлись в диск
apt install sysstat -y
iostat -x 1 5
Смотрите на %util — если близко к 100, диск загружен полностью. Растущий await означает, что операции ждут очереди.
Кто именно пишет:
apt install iotop -y
iotop -o
Типичные виновники: бэкап, запущенный в рабочее время, переиндексация, логи в режиме отладки, забытый debug.log на несколько гигабайт.
Проверьте размер логов — они разрастаются незаметно:
du -sh /var/log/*
Шаг 6. То, что помогает почти всегда
Если конкретного виновника не нашлось, а сайт всё равно небыстрый, три вещи дают результат в большинстве случаев.
Кэширование страниц. Готовая HTML-страница вместо сборки заново при каждом заходе. На WordPress это плагины кэша, на Bitrix — встроенный кэш, для остальных — кэш на уровне nginx. Разница обычно кратная.
Отдача статики через nginx. Картинки, стили и скрипты не должны проходить через PHP:
location ~* \.(jpg|jpeg|png|gif|webp|svg|css|js|woff2)$ {
expires 30d;
access_log off;
}
Сжатие ответа:
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;
Страница уезжает клиенту в несколько раз меньшим объёмом.
Полный рабочий конфиг с этими блоками: Как разместить сайт на VPS.
Шаг 7. Когда сервера действительно мало
Есть ситуации, когда оптимизировать больше нечего. Признаки:
- кэш настроен, медленных запросов нет, индексы на месте
- load average стабильно выше числа ядер круглосуточно
- память занята приложением, а не кэшем, и своп в работе
- рост посещаемости в разы за последние месяцы
Тогда вопрос решается не настройками, а ресурсами: переход на тариф выше занимает несколько минут и не требует переноса данных. Когда упираются уже и в топовый тариф, смотрят в сторону разнесения ролей или физической машины: Выделенный сервер или VPS.
Порядок диагностики кратко
| Что проверить | Команда | На что смотреть |
|---|---|---|
| Время ответа | curl -w "%{time_total}" |
больше 1 с — сервер |
| Нагрузка | uptime |
выше числа ядер |
| Память | free -h |
используется своп |
| Диск: место | df -h |
больше 90% |
| Диск: скорость | iostat -x 1 5 |
%util около 100 |
| Процессы | htop |
кто в топе |
| База | slow.log |
запросы дольше 1 с |
| Трафик | access.log |
один IP с тысячами запросов |
Частые вопросы
Сайт тормозит только вечером, в чём дело?
Либо пиковая посещаемость, либо совпадение с расписанием задач. Проверьте crontab -l — нередко это бэкап или выгрузка, запущенные в неудачное время.
Помогает ли перезагрузка сервера? Как временная мера — да, она сбрасывает зависшие процессы и освобождает память. Но если через день всё повторяется, причина не устранена, а спрятана.
Как понять, тормозит сервер или интернет у посетителя?
Замерьте время ответа командой curl с другой машины. Если сервер отвечает быстро, а у клиента медленно — дело в канале или в его устройстве.
Стоит ли ставить CDN? CDN ускоряет отдачу картинок и статики, особенно для географически далёких посетителей. Но он не лечит медленную генерацию страницы — динамика всё равно считается на вашем сервере.
Что делать, если ничего не помогло? Соберите вывод команд из таблицы выше и напишите в поддержку. По цифрам узкое место видно почти всегда, и это быстрее, чем перебирать настройки наугад.
Итог
Диагностика — это движение от общего к частному: время ответа, потом ресурсы, потом конкретный процесс, потом конкретный запрос. Такой порядок экономит часы по сравнению с угадыванием.
И два факта из практики: чаще всего виноваты отсутствие кэша и запрос к базе без индекса. Именно с них и стоит начинать, если времени на полную диагностику нет.

