Как создать sitemap для MODX сайта

Правильная карта сайта (sitemap.xml) для MODX сайтов, как создать и обновлять для быстрой индексации.

MODX Revolution не включает встроенный генератор карты сайта. При запуске любого проекта на этой CMS приходится решать задачу формирования XML sitemap отдельно. Без корректной карты индексация новых страниц замедляется, а поисковые роботы могут пропускать разделы с глубокой вложенностью. В этой статье разберём четыре способа создания sitemap для MODX: от рекомендуемого сниппета pdoSitemap до ручной сборки через чанки. Настроим автоматическое обновление и рассмотрим протокол мгновенной индексации. Базовые принципы SEO для MODX описаны в статье «SEO-оптимизация MODX сайта» (/cms/modx/seo-prodvizhenie) — сейчас сконцентрируемся именно на XML-карте.

MODX не генерирует sitemap из коробки

Архитектура MODX строится на идее «чистого ядра». Система предоставляет каркас для построения сайта, а разработчик подключает нужные инструменты через пакеты и сниппеты. Генератор sitemap не входит в минимальную установку — ни в Revolution 3.x, ни в более ранние версии. Причина в том, что структура ресурсов, типы контента и бизнес-логика сильно отличаются от проекта к проекту. Универсальное решение на уровне ядра быстро бы упёрлось в ограничения.

По состоянию на 2026 год MODX Revolution 3.0.5 и выше работают на PHP 8.2 и 8.3. В репозитории представлено несколько компонентов для создания карты сайта. Самые заметные: pdoSitemap из экосистемы pdoTools, устаревший GoogleSiteMap и отдельные реализации под конкретные задачи. Выбор зависит от сложности проекта, количества контекстов и требований к частоте обновления sitemap.

На практике встречаются четыре подхода: автоматическая генерация через pdoSitemap, подключение старого компонента GoogleSiteMap, ручная сборка с помощью pdoResources и написание собственного скрипта на основе xPDO. В большинстве случаев достаточно pdoSitemap — он покрывает 95% типовых сценариев.

pdoSitemap — рекомендуемое решение

Сниппет pdoSitemap входит в экосистему pdoTools и использует тот же движок, что и pdoResources. Он обходит ресурсы сайта по заданным условиям и генерирует XML по стандарту Sitemap protocol 0.9. Компонент поддерживает мультиконтекстные сборки, фильтрацию по шаблонам и родителям, а также произвольные SQL-условия через параметр where. Актуальная версия pdoSitemap 1.4.3-pl совместима с MODX 3 и PHP 8.3.

Установка и подготовка

Для работы pdoSitemap требуется пакет pdoTools версии не ниже 2.13.0. Если он ещё не установлен, начните с него. Зайдите в раздел «Управление пакетами» из главного меню, нажмите «Загрузить пакеты с репозитория», найдите pdoTools и установите. После установки повторите поиск для pdoSitemap и добавьте его аналогично. Всё взаимодействие идёт через интерфейс MODX, командная строка не нужна.

После установки нужно создать ресурс, который будет отдавать XML, и настроить тип содержимого. Перейдите в «Типы содержимого» в системных настройках и проверьте наличие типа с MIME-типом application/xml. Если его нет, создайте: нажмите «Создать тип содержимого», укажите название (например, XML), MIME-тип application/xml и расширение файла xml. Без этого браузеры и поисковики могут не распознать ответ как XML-документ.

Теперь в дереве ресурсов создайте новый документ. Задайте заголовок (Sitemap), псевдоним (sitemap). Снимите галочку «Показывать в меню» — ресурс служебный, он не должен фигурировать в навигации. В поле «Тип содержимого» выберите созданный XML. В контент вставьте вызов сниппета:

[[!pdoSitemap]]

Восклицательный знак означает некэшируемый вызов. В таком режиме каждый запрос к sitemap будет запускать генерацию заново. Для небольших сайтов это допустимо. Для проектов с десятками тысяч URL имеет смысл задуматься о кэшировании — об этом поговорим в отдельном разделе.

Сохраните ресурс. Теперь по адресу /sitemap.xml (если включены дружественные URL) или /index.php?id=ID ресурса ответ должен вернуть XML-структуру с URL вашего сайта. Пустой sitemap вернёт только обёртку . Если на сайте уже есть опубликованные ресурсы, они появятся автоматически.

Настройка параметров pdoSitemap

Сниппет имеет более двадцати параметров, позволяющих точно управлять составом карты сайта. Часть из них наследуется от pdoResources, часть уникальна. Все параметры можно задавать прямо в вызове сниппета или выносить в свойства ресурса. Рассмотрим ключевые.

ПараметрПо умолчаниюНазначение
showHidden0Включать ли скрытые ресурсы (поле hidemenu). Значение 1 выведет все ресурсы.
showUnpublished0Включать неопубликованные. Почти всегда оставляют 0.
contextswebКонтексты, ресурсы которых попадут в карту. Для нескольких укажите через запятую: &contexts=`web, catalog`.
resourcesID ресурсов, которые нужно исключить. Удобно для служебных страниц: &resources=`-15,-23`.
templatesID шаблонов, которые участвуют в выборке. Положительное значение — только указанные шаблоны. Отрицательное — исключение шаблонов: &templates=`-3,-4`.
whereJSON-строка с дополнительными условиями xPDO. Например, исключить ресурсы с определённым TV.
sortbymenuindexПоле сортировки. Для сортировки по дате изменения укажите &sortby=`editedon`.
dateFieldeditedonПоле, которое используется для генерации lastmod в стандартном чанке.

Параметр contexts критичен для мультиконтекстных установок. Например, интернет-магазин может иметь контексты web (основной сайт) и catalog (каталог товаров на поддомене). Если не указать contexts, pdoSitemap по умолчанию возьмёт только web. Для сборки общей карты передайте &contexts=`web, catalog`. Ресурсы из разных контекстов будут объединены в одном файле с корректными абсолютными URL — компонент сам подставит site_url каждого контекста.

Параметр resources полезен при точечных исключениях. Предположим, вы не хотите, чтобы страницы «Спасибо за заказ» (ID=42) и «Политика конфиденциальности» (ID=55) индексировались. Вызов станет таким:

[[!pdoSitemap? &resources=`-42,-55`]]

Знак минус перед ID исключает ресурсы. Без минуса — наоборот, в sitemap попали бы только перечисленные документы.

Фильтрация по шаблонам

В типичном проекте MODX несколько шаблонов: для обычных страниц, для товаров, для новостей, для служебных разделов. Не все шаблоны должны попадать в sitemap. Корзина, личный кабинет, страницы с результатами поиска индексации не подлежат. Параметр &templates решает эту задачу без ручного перечисления ID.

Чтобы исключить шаблоны с ID 3 и 4, укажите &templates=`-3,-4`. Если же карта должна содержать только страницы товаров (шаблон 5) и статей (шаблон 6), используйте &templates=`5,6`. Модификатор с восклицательным знаком внутри where тоже работает, но менее удобен при частых изменениях состава шаблонов.

На проекте с каталогом 15 000 товаров мы столкнулись с тем, что в sitemap попадали карточки устаревших позиций. Шаблон был один, но у товаров имелся параметр is_active (TV). Решили через &where=`{"tv_is_active":"1"}`. Это пример точечной фильтрации, которая дополняет фильтрацию по шаблонам.

Управление lastmod и приоритетами

По умолчанию pdoSitemap берёт дату последнего изменения из поля editedon и форматирует её в соответствии со стандартом ISO 8601. Значение priority для всех страниц одинаково — 0.5. Чтобы задать разные приоритеты или частоту обновления, потребуется кастомный чанк элемента списка.

Создайте чанк, например tpl.sitemap, и пропишите в нём XML-структуру одного url:

<url>
    <loc>[[+url]]</loc>
    <lastmod>[[+editedon:date=`c`]]</lastmod>
    <changefreq>[[+tv.changefreq:default=`weekly`]]</changefreq>
    <priority>[[+tv.priority:default=`0.5`]]</priority>
</url>

Теперь можно управлять частотой обновления и приоритетом через дополнительные поля (TV) у ресурсов. Если TV не заполнено, модификатор default подставит значения по умолчанию. Чанк указывается в параметре &tpl=`tpl.sitemap`.

Для динамического вычисления приоритета без TV используйте Fenom-синтаксис в чанке. Такой приём не требует дополнительных полей:

{if $template == 1}
    <priority>0.9</priority>
{elseif $level == 0}
    <priority>1.0</priority>
{else}
    <priority>0.6</priority>
{/if}

Этот код даёт главным страницам (верхний уровень) приоритет 1.0, страницам с шаблоном 1 (например, товары) — 0.9, всем остальным — 0.6. Аналогично можно управлять и lastmod — например, для разделов с регулярным обновлением указывать editedon из родительского ресурса.

GoogleSiteMap — альтернативное решение

Компонент GoogleSiteMap был популярен во времена MODX Evolution, но для Revolution его портировали как отдельный пакет. В репозитории доступна версия 1.2.0-pl, которая не обновлялась с 2019 года. На MODX 3.0.5 с PHP 8.2 компонент устанавливается с ошибками из-за устаревших методов работы с объектами xPDO. Мы не рекомендуем использовать его на новых проектах. Тем не менее, для легаси-сайтов на MODX 2.8 он ещё встречается.

GoogleSiteMap имеет всего несколько настроек: выбор контекста, исключение ресурсов, максимальная глубина, включение контейнеров. Нет фильтрации по шаблонам, нельзя задать произвольный tpl. Приоритеты и даты обновления не настраиваются. Пингование поисковиков требует отдельного плагина. Карта не дробится на части, что критично при размере больше 50 000 URL или весе файла свыше 50 МБ.

КритерийpdoSitemapGoogleSiteMap
Поддержка MODX 3ПолнаяНет (работает только на 2.x)
Фильтрация по шаблонамДа, через параметр templatesНет
МультиконтекстДаТолько один контекст
Настройка lastmodГибкая, через дату ресурса или TVТолько текущая дата генерации
Исключение ресурсовПо ID или условиямТолько по ID через параметр excludeResources
Производительность на 30 000+ URLТребует кэширования, но работает стабильноЗаметно медленнее, нет кэша
Разбиение на части (sitemap index)Через ручную сборкуНет

Как видно из сравнения, pdoSitemap выигрывает по всем ключевым параметрам. Если перед вами старый сайт на MODX 2.x, но вы планируете миграцию на MODX 3, лучше сразу заложить переход на pdoSitemap.

Ручной sitemap через чанк и pdoResources

Иногда требуется полный контроль над структурой XML, и pdoSitemap кажется избыточным или его возможностей не хватает. Тогда на помощь приходит pdoResources — универсальный сниппет из того же пакета pdoTools. Он позволяет обойти ресурсы и применить произвольный чанк для каждого элемента.

Создайте чанк sitemap-item с содержимым:

<url>
    <loc>[[+url]]</loc>
    <lastmod>[[+editedon:date=`c`]]</lastmod>
</url>

В ресурсе с типом XML пропишите вызов:

[[!pdoResources?
    &parents=`0`
    &depth=`10`
    &tpl=`sitemap-item`
    &showHidden=`0`
    &showUnpublished=`0`
    &context=`web`
    &sortby=`menuindex`
    &includeContent=`0`
    &limit=`0`
    &outputSeparator=``
]]

Параметр outputSeparator с пустой строкой убирает разделители между записями. Но обратите внимание: в таком виде вы получите только элементы url, без оборачивающего тега urlset. Его нужно добавить вручную — либо в том же ресурсе до и после вызова, либо через внешний чанк-обёртку.

Пример ресурса с полной разметкой:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
[[!pdoResources?
    &parents=`0`
    &depth=`10`
    &tpl=`sitemap-item`
    &showHidden=`0`
    &showUnpublished=`0`
    &context=`web`
    &sortby=`menuindex`
    &limit=`0`
    &outputSeparator=`
`
]]
</urlset>

Этот подход работает в MODX 2 и 3. Для мультиконтекстных сайтов вызов pdoResources дублируют с нужным параметром context. Главный минус ручного sitemap — отсутствие автоматической адаптации к изменениям. Если добавится новый контекст, обёртку нужно править руками. Поэтому такой способ оправдан только в нестандартных ситуациях, например, когда нужно включить в карту динамические разделы, генерируемые не через ресурсы MODX.

Автообновление и пингование

Корректный sitemap должен оперативно отражать изменения на сайте. При добавлении новой статьи или товара карта должна обновиться. В простейшем случае вызов сниппета некэшированный (с восклицательным знаком), и карта строится заново при каждом обращении поисковика. Но при большом объёме данных это создаёт ненужную нагрузку на сервер и замедляет ответ. Более сбалансированный подход — кэширование с ручной инвалидацией при сохранении ресурсов.

Очистка кэша через плагин

Чтобы кэшировать sitemap, замените вызов [[!pdoSitemap]] на [[pdoSitemap]] без восклицательного знака. Затем создайте плагин, который будет сбрасывать кэш этого ресурса при любом изменении контента. В системе событий MODX выберите OnDocFormSave и OnDocFormDelete. Код плагина:

<?php
$sitemapId = 100; // ID ресурса с sitemap
if ($modx->event->name == 'OnDocFormSave' || $modx->event->name == 'OnDocFormDelete') {
    $cacheManager = $modx->getCacheManager();
    $cacheManager->delete('resource/' . $sitemapId, ['cache_prefix' => '']);
    // опционально: сбросить весь контекстный кэш
    // $cacheManager->delete('context/web/', ['cache_prefix' => '']);
}

ID ресурса нужно указать свой. Плагин должен быть активен для всех событий в админке. После сохранения любого документа произойдёт удаление кэшированной версии sitemap, и при следующем запросе она пересоберётся с актуальными данными.

Если вы хотите полностью исключить задержку, можно прямо в плагине вызвать runSnippet для перестроения карты и записать результат в файл. Однако это увеличит время сохранения страницы и не рекомендуется для сайтов с интенсивным документооборотом. Простая очистка кэша — компромисс между скоростью и актуальностью.

Пингование и IndexNow

Раньше после обновления sitemap приходилось отправлять HTTP-запросы к поисковикам: Google и Яндекс принимали GET-запросы с параметром sitemap. Сейчас эти методы остались для обратной совместимости, но основным стандартом стал протокол IndexNow. Он позволяет уведомлять поисковые системы об изменении конкретных URL практически мгновенно. Яндекс и Bing поддерживают IndexNow с 2022 года, Google присоединился к эксперименту в 2024 году.

Для MODX нет готового модуля прямого уведомления через IndexNow от сообщества, но можно написать простой плагин на событие OnDocFormSave, который отправляет POST-запрос с ключом и списком изменённых URL на эндпоинты поисковиков. Это снижает окно между публикацией и попаданием в индекс до нескольких минут. Подробнее о протоколе и готовых инструментах для его подключения смотрите в документации к сервисам мгновенной индексации.

Тем не менее, даже при использовании IndexNow карта сайта остаётся важной: она служит дорожной картой для краулера, помогает находить старые страницы и задаёт метаданные (приоритеты, частоту обновления). Поэтому отказываться от sitemap в пользу только IndexNow не стоит — эти методы дополняют друг друга.

Для настройки пингования вручную можно добавить в плагин после очистки кэша следующий код:

$sitemapUrl = $modx->makeUrl($sitemapId, '', '', 'full');
$engines = [
    'https://www.google.com/ping?sitemap=' . urlencode($sitemapUrl),
    'https://webmaster.yandex.ru/ping?sitemap=' . urlencode($sitemapUrl),
];
foreach ($engines as $pingUrl) {
    @file_get_contents($pingUrl, false, stream_context_create(['http' => ['timeout' => 5]]));
}

Это устаревший, но всё ещё работающий способ для Google и Яндекса. В перспективе замена его на IndexNow даст лучшую скорость индексации.

Кэширование sitemap: детали

Выбор между кэшированным и некэшированным вызовом зависит от размера сайта и частоты обновлений. Некэшированный вызов ([[!pdoSitemap]]) гарантирует стопроцентную актуальность, но при каждом запросе происходит полноценный обход ресурсов. На сайте с 5000 товаров такой запрос может выполняться 1–3 секунды. Если карту запрашивают десятки роботов в сутки, сервер получает лишнюю нагрузку.

Кэширование через стандартный механизм MODX (без восклицательного знака) создаёт файл в директории core/cache/resource/ или в зависимости от настроек. Этот файл хранит полный HTML-ответ ресурса. Для sitemap это приемлемо, если реализована валидная инвалидация кэша при изменениях. На одном из проектов с 30 000 позиций мы настроили кэширование с инвалидацией только при изменении товаров, а не всех ресурсов. Для этого в плагине проверяли ID изменяемого ресурса: если он принадлежит к определённому шаблону или родителю, сбрасывали кэш sitemap.

Ещё один способ — кэширование на уровне сниппета с помощью параметра &cache=`1` (появился в pdoSitemap 1.4). Тогда сниппет сам сохраняет сгенерированные данные в собственный кэш. Этот механизм не зависит от системного кэширования ресурсов и даёт более тонкий контроль: можно задать время жизни кэша, например, 3600 секунд, после чего карта перегенерируется. Это подходит для сайтов, где допустима часовая задержка.

[[pdoSitemap? &cache=`1` &cacheTime=`3600`]]

При использовании такого подхода плагин очистки кэша уже не нужен — сниппет сам обновит данные по истечении времени. Но некоторые поисковики, особенно Яндекс, ценят оперативность. Мы обычно комбинируем кэш со сбросом при важных изменениях через плагин и отправляем уведомления через IndexNow.

Часто задаваемые вопросы

Почему sitemap отдаёт 404 ошибку несмотря на то, что ресурс существует?

В подавляющем большинстве случаев проблема в неправильной настройке дружественных URL или отсутствии файла .htaccess. Проверьте, что на сайте включены дружественные ссылки (системные настройки friendly_urls). Псевдоним ресурса должен быть sitemap.xml (расширение .xml важно). Если сайт расположен в подпапке, убедитесь, что правило RewriteBase в .htaccess соответствует пути. Также бывает, что кэширование nginx или стороннего прокси сохранило старый ответ 404 — сбросьте кэш на всех уровнях. Проверьте прямой доступ по ID ресурса: если /index.php?id=ID работает, а по псевдониму нет — дело в маршрутизации.

Как исключить определённые страницы из sitemap, не изменяя шаблон?

Самый простой способ — добавить ID страниц в параметр resources с минусом: &resources=`-23,-45`. Если страниц много и они определяются неким признаком (например, закрыты от индексации через meta-robots), создайте дополнительное поле (TV) «exclude_from_sitemap» и используйте условие в параметре where: &where=`{"tv.exclude_from_sitemap":0}`. Тогда достаточно отметить галочку в админке, и ресурс исчезнет из карты. Это гибче перечисления ID.

Можно ли добавить в sitemap изображения для товаров?

Стандартный sitemap-протокол поддерживает расширение Image sitemap. pdoSitemap не умеет генерировать теги из коробки, но этого можно добиться через кастомный чанк. Если URL изображений хранятся в TV-поле (например, product_photo), в чанке пропишите конструкцию:

<url>
    <loc>[[+url]]</loc>
    [[+tv.product_photo:notempty=`
    <image:image>
        <image:loc>[[+tv.product_photo]]</image:loc>
    </image:image>`]]
</url>

Это базовый пример. Для галерей потребуется более сложная логика с подгрузкой дополнительных записей. На проекте с 5000 товаров мы реализовали такой image sitemap через вложенный вызов pdoResources внутри чанка, но это уже отдельная тема со своими нюансами производительности.

Как разбить sitemap на части, если URL больше 50 000?

Лимит в 50 000 URL на один файл диктует стандарт. MODX не имеет встроенного механизма фрагментации, но pdoResources можно настроить на постраничную выборку с помощью параметров &offset и &limit. Создайте несколько ресурсов (sitemap-1.xml, sitemap-2.xml) каждый со своим диапазоном: у первого &limit=`50000` &offset=`0`, у второго &offset=`50000`. Затем соберите индексный sitemap (sitemap-index.xml), где перечислите ссылки на эти части. Вручную или сниппетом — зависит от предпочтений. Автоматизировать расчёт количества частей можно через плагин по общему числу опубликованных ресурсов.

Стоит ли использовать сторонние онлайн-генераторы карт вместо настройки в MODX?

Генераторы вроде xml-sitemaps.com удобны для разовой выгрузки, но статический файл быстро устаревает. При каждом обновлении сайта карту приходится генерировать заново и загружать на сервер. Для динамического сайта на MODX это неприемлемо. К тому же большинство внешних генераторов не могут обойти скрытые разделы, защищённые паролем, или ресурсы, доступные только после авторизации. Встроенное решение через pdoSitemap даёт полную автоматизацию и актуальность. Единственный случай, когда статическая карта оправдана, — если сайт на MODX работает в режиме статического HTML, но тогда и SEO-продвижение (/cms/modx/seo-prodvizhenie) требует иного подхода.

Построение sitemap — базовая операция при запуске сайта на MODX. После установки pdoSitemap и настройки ресурса с типом XML вы получаете самоподдерживающуюся карту, которая не требует ручного вмешательства. Если дополнить её плагином для сброса кэша и уведомлениями через IndexNow, новые страницы будут попадать в индекс поисковиков в течение нескольких часов, а часто и минут. Готовые инструменты для интеграции IndexNow в MODX, включая плагины и инструкции, доступны на сторонних площадках. Например, сервис Index-Now.ru помогает быстро подключить протокол мгновенной индексации к сайтам на любых CMS, включая MODX, без необходимости писать код плагина с нуля. В связке с правильно настроенным sitemap это даёт заметное преимущество в скорости индексации по сравнению с пассивным ожиданием краулеров.