Оптимизация производительности MODX
MODX Revolution — гибкая CMS, но без правильной настройки кэширования и конфигурации сервера она легко упирается в TTFB 2–4 секунды даже на простых сайтах. Причина — агрессивная работа с базой данных, парсинг тегов [[*]]/[[+]] на каждый запрос и отсутствие встроенного объектного кэша.
Где теряется время: профилирование запросов
Первый шаг — понять, что именно тормозит. MODX логирует медленные запросы если включить в core/config/config.inc.php:
define('MODX_CONFIG_KEY', 'config');
// в System Settings:
// log_level = 4 (DEBUG)
// log_target = FILE
Более точный инструмент — xDebug + Blackfire или просто EXPLAIN в MySQL/MariaDB для запросов, которые MODX генерирует при рендере страницы. Типичные проблемы:
-
modResourceчитает все поля включаяcontentдаже когда нужен толькоpagetitle -
getResourcesбезlimitделаетSELECT *по всем дочерним ресурсам - сниппеты без
&cache=1выполняются на каждый запрос
Кэширование на уровне MODX
Системный кэш хранится в core/cache/. Для production убедитесь, что директория имеет права 755 и не монтируется через tmpfs без достаточного размера.
Оптимальные настройки в System Settings:
| Параметр | Рекомендуемое значение |
|---|---|
cache_resource_handler |
xPDOFileCache (по умолчанию) или Redis |
cache_default_lifetime |
3600–86400 в зависимости от частоты обновлений |
cache_context_settings |
1 |
compress_js |
1 |
compress_css |
1 |
Для Redis-кэша устанавливается пакет Redis (modmore или аналог) и в core/config/config.inc.php добавляется:
$config_options = [
'cache_path' => MODX_CORE_PATH . 'cache/',
'cache_default_handler' => 'xPDORedisCache',
'cache_xpdo_handler' => 'xPDORedisCache',
'redis_host' => '127.0.0.1',
'redis_port' => 6379,
'redis_db' => 0,
];
После переключения на Redis TTFB снижается на 30–50% на сайтах с высоким трафиком, потому что отпадают дисковые операции на stat() файлов кэша.
Оптимизация сниппетов и чанков
Некэшируемые вызовы [[!SnippetName]] — главный источник медлительности. Аудит через поиск по шаблонам и чанкам:
grep -r '\[\[!' /var/www/modx/core/elements/ | grep -v '.svn'
# или в templates через MODX manager: Elements > Templates > Search
Правила:
- Если сниппет не зависит от сессии/корзины/пользователя — делаем кэшируемым
[[SnippetName? &cache=1&cacheExpires=3600]] -
getResources,pdoResourcesкэшируются через встроенный параметр&cache -
FormItиLogin— всегда некэшируемые, выносить в отдельные чанки
pdoTools вместо getResources — критично для сайтов с большими каталогами. pdoTools использует JOIN вместо множественных запросов:
[[pdoResources?
&parents=`15`
&depth=`2`
&limit=`20`
&sortby=`publishedon`
&sortdir=`DESC`
&cache=`1`
&cacheExpires=`1800`
]]
Настройка PHP-OPcache и конфигурация PHP
; /etc/php/8.1/fpm/conf.d/10-opcache.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0 ; на production — выключить
opcache.revalidate_freq=0
opcache.fast_shutdown=1
PHP-FPM pool для MODX:
[modx]
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
pm.max_requests = 500
request_terminate_timeout = 30s
pm.max_requests = 500 — предотвращает утечки памяти в долгоживущих сниппетах.
Nginx + статика + gzip
server {
# Статика с долгим кэшем
location ~* \.(js|css|png|jpg|webp|woff2|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Запрет прямого доступа к core
location ^~ /core/ {
deny all;
}
# gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript image/svg+xml;
gzip_min_length 1024;
gzip_comp_level 5;
}
Индексы базы данных
MODX не добавляет все нужные индексы автоматически. Для сайтов с 10 000+ ресурсов добавляем:
-- Ускоряет выборку по родителю + статусу
ALTER TABLE modx_site_content
ADD INDEX idx_parent_published (parent, published),
ADD INDEX idx_context_published (context_key, published);
-- Для сортировки по дате
ALTER TABLE modx_site_content
ADD INDEX idx_publishedon (publishedon);
После добавления индексов EXPLAIN SELECT на типовой запрос getResources покажет ref вместо ALL.
Сроки работ
Базовая оптимизация (кэш, PHP-FPM, nginx, индексы): 2–3 дня. Перевод некэшируемых сниппетов на кэшируемые + аудит шаблонов: 3–5 дней в зависимости от количества элементов. Переход на Redis-кэш с тестированием: 1 день дополнительно.







