Registering custom URL types, controlling priority, and automating submission
Magento's sitemap module already covers products, categories and CMS pages, but there is no ready made item provider for custom landing pages or other project specific URL types. This article shows how to register a custom item provider, how to control priority and change frequency per content type, and how to automate multi store sitemap generation together with submission to Google Search Console.
Table of Contents
- 1. What the default sitemap covers, and what it does not
- 2. The item provider architecture of the sitemap module
- 3. Implementing a custom item provider for landing pages
- 4. Making priority and change frequency configurable per content type
- 5. Configuring multi store sitemap generation
- 6. Automating submission to Google Search Console
- 7. Adding hreflang alternate links for multilingual stores
- 8. Validating and monitoring the generated sitemap
- 9. Pitfalls from real projects
- 10. Summary
- 11. FAQ
1. What the default sitemap covers, and what it does not
The native Magento sitemap covers three content types: products, categories and CMS pages, each through its own item provider. Anything outside those three types, such as standalone landing pages from a custom module, brand overview pages or generated PDF guide pages, gets no automatic entry in the sitemap.
That clearly sets this topic apart from URL rewrites, covered elsewhere. A URL rewrite makes sure a page is reachable under a readable URL at all, but says nothing about whether that URL also shows up in the sitemap and therefore gets actively offered to search engines for crawling.
Anyone introducing new URL types, for example as part of a custom content module, should think from the start about how those URLs reach the sitemap, instead of relying purely on organic crawling without a sitemap entry.
2. The item provider architecture of the sitemap module
The central piece is the ItemProviderInterface with a getItems method that returns a list of SitemapItemInterface objects. Each object carries a URL, a modification date, a priority and a change frequency. All registered providers get merged through ItemProviderComposite, which is itself just a simple aggregator adding up the results from every configured provider into one list.
New providers get registered declaratively through di.xml, by adding an entry to the itemProviders array on the virtual type of ItemProviderComposite. Important in day to day work: after changing that array, setup:di:compile has to run, sometimes even twice, since an already compiled container can otherwise silently ignore the new entry, even in developer mode.
<virtualType name="Magento\Sitemap\Model\ItemProvider\ItemProviderComposite">
<arguments>
<argument name="itemProviders" xsi:type="array">
<item name="category" xsi:type="object">Magento\Sitemap\Model\ItemProvider\CategoryItemProvider</item>
<item name="product" xsi:type="object">Magento\Sitemap\Model\ItemProvider\ProductItemProvider</item>
<item name="cms_page" xsi:type="object">Magento\Sitemap\Model\ItemProvider\CmsPageItemProvider</item>
<item name="landingpage" xsi:type="object">Mironsoft\CustomSitemap\Model\ItemProvider\LandingPageItemProvider</item>
</argument>
</arguments>
</virtualType>
3. Implementing a custom item provider for landing pages
A custom provider reads its own landing page entity, filters for active entries approved for search, and builds SitemapItemInterface objects with a complete, absolute URL from that data. It matters that the modification dates come from the entity's actual updated_at column rather than a static timestamp, so the sitemap honestly reflects when a page last changed.
Priority and change frequency should not be hard coded but configurable through system.xml, mirroring the fields Magento already offers for products, categories and CMS pages under Stores, Configuration, XML Sitemap. That allows adjusting how new URL types get weighted later on without a code deploy.
<?php
declare(strict_types=1);
namespace Mironsoft\CustomSitemap\Model\ItemProvider;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Sitemap\Model\ItemProvider\ItemProviderInterface;
use Magento\Sitemap\Model\SitemapItemInterfaceFactory;
use Mironsoft\CustomSitemap\Model\ResourceModel\LandingPage\CollectionFactory;
/**
* Provides active custom landing pages as additional entries for the XML sitemap.
*/
class LandingPageItemProvider implements ItemProviderInterface
{
private const XML_PATH_PRIORITY = 'sitemap/landingpage/priority';
private const XML_PATH_CHANGEFREQ = 'sitemap/landingpage/changefreq';
/**
* @param CollectionFactory $collectionFactory
* @param SitemapItemInterfaceFactory $itemFactory
* @param ScopeConfigInterface $scopeConfig
*/
public function __construct(
private readonly CollectionFactory $collectionFactory,
private readonly SitemapItemInterfaceFactory $itemFactory,
private readonly ScopeConfigInterface $scopeConfig,
) {
}
/**
* Builds sitemap entries for every active landing page of the given store.
*
* @param int $storeId
* @return \Magento\Sitemap\Model\SitemapItemInterface[]
*/
public function getItems($storeId): array
{
$priority = (float) $this->scopeConfig->getValue(self::XML_PATH_PRIORITY, 'store', $storeId);
$changeFreq = (string) $this->scopeConfig->getValue(self::XML_PATH_CHANGEFREQ, 'store', $storeId);
$collection = $this->collectionFactory->create();
$collection->addFieldToFilter('is_active', ['eq' => 1])
->addFieldToFilter('store_id', ['eq' => $storeId]);
$items = [];
foreach ($collection as $landingPage) {
$items[] = $this->itemFactory->create([
'url' => $landingPage->getUrlKey(),
'updatedAt' => $landingPage->getUpdatedAt(),
'images' => [],
'priority' => $priority,
'changeFrequency' => $changeFreq,
]);
}
return $items;
}
}
4. Making priority and change frequency configurable per content type
For the configurable values, it is worth adding a dedicated system.xml section inside the existing XML Sitemap group in the admin, with select fields for change frequency, mirroring the values always, hourly, daily, weekly, monthly, yearly and never that the sitemap standard already defines. Priority gets maintained as a decimal value between zero and one.
In practice, it has proven useful to give landing pages with time limited campaign relevance a higher priority and a shorter change frequency than evergreen content, so search engines get a hint to recrawl those pages more often, even though priority and change frequency ultimately only count as a signal for search engines, not a guarantee.
5. Configuring multi store sitemap generation
Every sitemap configuration in Magento is bound to a store, so every store that needs its own sitemap requires its own entry under Marketing, SEO and Search, Site Map, with its own file name and path. A common mistake is reusing the same file name across multiple stores, which makes the generated files overwrite each other.
The custom LandingPageItemProvider from the previous section has to filter store aware without exception, otherwise landing pages from one store wrongly show up in another store's sitemap as well, especially when several stores share the same database table with a store_id column.
The generate_sitemap cron job runs for all configured sitemaps in a single pass, which in practice keeps the timing offset between several store sitemaps down to a few seconds, consistent enough for most projects.
6. Automating submission to Google Search Console
Magento reliably generates the sitemap file, but it does not automatically submit it to Google Search Console, that stays a manual step in the Search Console interface by default. For automated submission, an additional cron job works well, calling the Search Console API with service account access after a successful sitemap run and reporting the generated URL through the sitemaps.submit endpoint.
The service account needs access rights on the relevant property verified in Search Console, usually through delegated access with a Google Cloud service identity created specifically for this purpose. This automation pays off especially when landing pages get created frequently and manually resubmitting after every campaign would be impractical.
# Trigger sitemap generation, then submit through a dedicated cron script
bin/magento sitemap:generate
# custom cron job then calls the Search Console API, e.g.
curl -X PUT \
"https://www.googleapis.com/webmasters/v3/sites/https%3A%2F%2Fwww.example.com%2F/sitemaps/sitemap.xml" \
-H "Authorization: Bearer $SEARCH_CONSOLE_TOKEN"
7. Adding hreflang alternate links for multilingual stores
The core sitemap module ships with no automatic hreflang alternate links between language variants of the same page, even though that is exactly what international multi store setups often want. That gap can be closed through a custom provider or a plugin on the existing providers that attaches additional alternate entries for the other language stores to each URL.
It matters that the mapping between language variants relies on a stable, language independent identifier, such as a shared landing page group ID, rather than a purely pattern based URL derivation, since translated slugs often differ substantially between languages and cannot be mapped into each other automatically.
8. Validating and monitoring the generated sitemap
After every extension with a new item provider, it is worth checking the generated file against Google's limits, a maximum of fifty thousand URLs or fifty megabytes uncompressed per file. Magento automatically splits into multiple files with a parent sitemap index once those limits get exceeded, but a custom provider adding many extra URLs can trigger that split point earlier than expected.
For ongoing operation, a simple monitor on the generate_sitemap cron job through the standard cron history in the admin is worth setting up, complemented by a check that the most recently generated file actually contains entries of the new content type. A silent failure in a custom provider, such as an empty collection caused by a wrong filter, otherwise produces a seemingly successful but content wise incomplete sitemap.
9. Pitfalls from real projects
The most common pitfall is a forgotten setup:di:compile after extending the itemProviders array, which means the new provider simply never gets called despite a correct di.xml configuration, sometimes even in developer mode, since already compiled container definitions can silently ignore the new entry. In practice it helps to run the compile step twice after such a change, just to be safe.
A second common mistake is missing store filtering in a custom provider, which causes landing pages from one store to accidentally appear in every store's sitemap. A third pitfall concerns forgotten deactivation logic, when a landing page gets deactivated or deleted in the admin, the next sitemap run has to automatically remove that URL, otherwise dead links remain in the sitemap and get flagged as crawl errors by search engines.
| URL type | Item provider | Priority configurable? | Multi store capable? |
|---|---|---|---|
| Products | ProductItemProvider (core) | Yes, via XML Sitemap configuration | Yes, natively |
| Categories | CategoryItemProvider (core) | Yes, via XML Sitemap configuration | Yes, natively |
| CMS pages | CmsPageItemProvider (core) | Yes, via XML Sitemap configuration | Yes, natively |
| Custom landing pages | Custom provider (this article) | Yes, via dedicated system.xml fields | Yes, with explicit store filtering |
| Hreflang alternates | Custom plugin on existing providers | Not separately, follows the base URL | Yes, central for linking language stores |
Mironsoft
Magento development, module consulting, and system architecture
A Magento project that needs a second opinion or experienced execution?
We build custom Magento modules, advise on architecture decisions, and take on complex implementations, from service contract planning to production-ready deployment.
Architecture Consulting
Have module and system architecture thought through properly before you build.
Custom Module Development
Build custom Magento modules cleanly, following best practices.
Code Review & Audit
Have existing modules reviewed for performance, security, and maintainability.
10. Summary
XML sitemap
Architecture
ItemProviderInterface implementations supply sitemap items, ItemProviderComposite merges every registered provider.
Extension
New URL types get added through the itemProviders array in di.xml, after which setup:di:compile is mandatory.
Configuration
Priority and change frequency should be configurable through dedicated system.xml fields instead of being hard coded.
Automation
An additional cron job can automatically submit the generated sitemap through the Search Console API.