Die XML-Sitemap in Magento 2 erweitern und anpassen
AI generated
M2
di.xml
Magento 2
Die XML-Sitemap erweitern und anpassen
Eigene URL-Typen registrieren, Prioritäten steuern und die Übermittlung automatisieren

Magentos Sitemap-Modul deckt Produkte, Kategorien und CMS-Seiten bereits ab, für Custom-Landingpages oder andere projektspezifische URL-Typen fehlt jedoch ein fertiger ItemProvider. Dieser Artikel zeigt, wie sich ein eigener ItemProvider registrieren lässt, wie Priorität und Änderungsfrequenz pro Content-Typ gesteuert werden und wie sich Multi-Store-Sitemap-Generierung und die Meldung an die Google Search Console automatisieren lassen.

10 Min. Lesezeit Sitemap ItemProviderInterface Multi-Store Search Console

1. Was die Standard-Sitemap abdeckt und was fehlt

Die native Magento-Sitemap deckt drei Content-Typen ab: Produkte, Kategorien und CMS-Seiten, jeweils über einen eigenen ItemProvider. Für alles, was außerhalb dieser drei Typen liegt, etwa eigenständige Landingpages eines Custom-Moduls, Marken-Übersichtsseiten oder generierte PDF-Ratgeber-Seiten, gibt es keinen automatischen Eintrag in der Sitemap.

Das unterscheidet dieses Thema klar von URL-Rewrites, die bereits an anderer Stelle behandelt werden. Ein URL-Rewrite sorgt dafür, dass eine Seite überhaupt unter einer sprechenden URL erreichbar ist, sagt aber nichts darüber aus, ob diese URL auch in der Sitemap auftaucht und damit aktiv Suchmaschinen zum Crawlen angeboten wird.

Wer neue URL-Typen einführt, etwa im Rahmen eines eigenen Content-Moduls, sollte deshalb von Anfang an mitdenken, wie diese URLs in die Sitemap gelangen, statt sich ausschließlich auf organisches Crawling ohne Sitemap-Eintrag zu verlassen.

2. Die ItemProvider-Architektur des Sitemap-Moduls

Zentral ist das Interface ItemProviderInterface mit der Methode getItems, die eine Liste von SitemapItemInterface-Objekten liefert. Jedes Objekt trägt eine URL, ein Änderungsdatum, eine Priorität und eine Änderungsfrequenz. Alle registrierten Provider werden über ItemProviderComposite zusammengeführt, das selbst wieder nur ein einfacher Aggregator ist, der die Ergebnisse aller konfigurierten Provider zu einer Liste addiert.

Die Registrierung neuer Provider erfolgt deklarativ über di.xml, indem der virtuelle Typ von ItemProviderComposite um einen zusätzlichen Eintrag im itemProviders-Array ergänzt wird. Wichtig für den Alltag: Nach einer Änderung an diesem Array muss setup:di:compile laufen, teils sogar zweimal, da ein bereits kompilierter Container den neuen Eintrag sonst stillschweigend ignoriert, auch im 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. Eigenen ItemProvider für Custom-Landingpages implementieren

Ein eigener Provider liest die eigene Landingpage-Entität, filtert auf aktive, für die Suche freigegebene Einträge und baut daraus SitemapItemInterface-Objekte mit vollständiger, absoluter URL. Wichtig ist, dass die Änderungsdaten aus der tatsächlichen updated_at-Spalte der Entität stammen, statt eines statischen Zeitstempels, damit die Sitemap ehrlich widerspiegelt, wann sich eine Seite tatsächlich zuletzt geändert hat.

Priorität und Änderungsfrequenz sollten nicht hart codiert, sondern über system.xml konfigurierbar sein, analog zu den Feldern, die Magento bereits für Kategorien, Produkte und CMS-Seiten unter Stores, Konfiguration, XML Sitemap anbietet. Das erlaubt es, die Gewichtung neuer URL-Typen später ohne Code-Deploy anzupassen.


<?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;

/**
 * Liefert aktive Custom-Landingpages als zusätzliche Einträge für die 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,
    ) {
    }

    /**
     * Baut Sitemap-Einträge für alle aktiven Landingpages des übergebenen Stores.
     *
     * @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. Priorität und Änderungsfrequenz pro Content-Typ konfigurierbar machen

Für die konfigurierbaren Werte lohnt sich ein eigener system.xml-Abschnitt innerhalb der bestehenden XML-Sitemap-Gruppe im Admin, mit Select-Feldern für die Änderungsfrequenz, analog zu den Werten always, hourly, daily, weekly, monthly, yearly und never, die der Sitemap-Standard ohnehin vorgibt. Die Priorität wird als Dezimalwert zwischen null und eins gepflegt.

In der Praxis hat es sich bewährt, Landingpages mit zeitlich begrenzter Kampagnen-Relevanz eine höhere Priorität und eine kürzere Änderungsfrequenz zu geben als evergreen-Content, damit Suchmaschinen den Hinweis erhalten, diese Seiten häufiger erneut zu crawlen, auch wenn Priorität und Änderungsfrequenz von Suchmaschinen letztlich nur als Signal, nicht als Garantie behandelt werden.

5. Multi-Store-Sitemap-Generierung konfigurieren

Jede Sitemap-Konfiguration in Magento ist an einen Store gebunden, daher muss für jeden Store, der eine eigene Sitemap benötigt, ein eigener Eintrag unter Marketing, SEO und Suche, Site Map angelegt werden, mit eigenem Dateinamen und eigenem Pfad. Ein häufiger Fehler ist, denselben Dateinamen für mehrere Stores zu verwenden, wodurch sich die generierten Dateien gegenseitig überschreiben.

Der eigene LandingPageItemProvider aus dem vorherigen Abschnitt muss dabei zwingend store-bewusst filtern, sonst tauchen Landingpages eines Stores fälschlich auch in der Sitemap eines anderen Stores auf, insbesondere wenn mehrere Stores dieselbe Datenbank-Tabelle mit einer store_id-Spalte teilen.

Der Cron-Job generate_sitemap läuft für alle konfigurierten Sitemaps in einem Durchlauf, wodurch sich der zeitliche Versatz zwischen mehreren Store-Sitemaps in der Praxis meist auf wenige Sekunden beschränkt, was für die meisten Projekte ausreichend konsistent ist.

6. Automatisierte Übermittlung an die Google Search Console

Magento generiert die Sitemap-Datei zuverlässig, meldet sie aber nicht automatisch an die Google Search Console, das bleibt standardmäßig ein manueller Schritt im Search-Console-Interface. Für eine automatisierte Übermittlung bietet sich ein zusätzlicher Cron-Job an, der nach erfolgreichem Sitemap-Lauf die Search-Console-API mit einem Service-Account-Zugriff aufruft und die generierte URL über den sitemaps.submit-Endpunkt meldet.

Der Service Account benötigt dafür Zugriffsrechte auf die jeweilige, in der Search Console verifizierte Property, üblicherweise über einen delegierten Zugriff mit einer eigens für diesen Zweck angelegten Google-Cloud-Service-Identität. Diese Automatisierung lohnt sich vor allem, wenn Landingpages häufig neu erstellt werden und eine manuelle Neuanmeldung nach jeder Kampagne unpraktikabel wäre.


# Sitemap-Generierung anstoßen, danach Search-Console-Meldung über ein eigenes Cron-Skript
bin/magento sitemap:generate

# eigener Cron-Job ruft anschliessend die Search Console API auf, z.B.
curl -X PUT \
  "https://www.googleapis.com/webmasters/v3/sites/https%3A%2F%2Fwww.example.de%2F/sitemaps/sitemap.xml" \
  -H "Authorization: Bearer $SEARCH_CONSOLE_TOKEN"

Das Kern-Sitemap-Modul kennt von Haus aus keine automatischen hreflang-Alternate-Links zwischen Sprachvarianten derselben Seite, obwohl genau das für internationale Multi-Store-Setups oft gewünscht ist. Diese Lücke lässt sich über einen eigenen Provider oder ein Plugin auf den bestehenden Providern schließen, das pro URL zusätzliche Alternate-Einträge für die jeweils anderen Sprach-Stores anhängt.

Wichtig dabei ist, dass die Zuordnung zwischen Sprachvarianten über eine stabile, sprachunabhängige Kennung erfolgen muss, etwa eine gemeinsame Landingpage-Gruppen-ID, statt über eine reine URL-Musterableitung, da sich übersetzte Slugs zwischen Sprachen häufig deutlich unterscheiden und sich nicht automatisch ineinander überführen lassen.

8. Validierung und Monitoring der generierten Sitemap

Nach jeder Erweiterung um einen neuen ItemProvider lohnt sich eine Prüfung der generierten Datei gegen die von Google vorgegebenen Grenzwerte, maximal fünfzigtausend URLs beziehungsweise fünfzig Megabyte unkomprimiert pro Datei. Magento splittet automatisch in mehrere Dateien mit einem übergeordneten Sitemap-Index, sobald diese Grenzen überschritten werden, ein eigener Provider mit vielen zusätzlichen URLs kann diesen Splitpunkt jedoch früher als erwartet auslösen.

Für den laufenden Betrieb empfiehlt sich ein einfaches Monitoring des generate_sitemap-Cron-Jobs über die Standard-Cron-Historie im Admin, ergänzt um eine Prüfung, ob die zuletzt generierte Datei tatsächlich Einträge des neuen Content-Typs enthält. Ein stiller Fehler im eigenen Provider, etwa eine leere Collection wegen eines falschen Filters, führt sonst zu einer scheinbar erfolgreichen, aber inhaltlich unvollständigen Sitemap.

9. Fallstricke aus der Praxis

Der häufigste Fallstrick ist ein vergessenes setup:di:compile nach der Ergänzung des itemProviders-Arrays, wodurch der neue Provider trotz korrekter di.xml-Konfiguration schlicht nicht aufgerufen wird, teils auch im Developer Mode, da bereits kompilierte Container-Definitionen den neuen Eintrag stillschweigend ignorieren können. In der Praxis hilft es, den Compile-Schritt nach solchen Änderungen zur Sicherheit zweimal auszuführen.

Ein zweiter, häufiger Fehler ist fehlende Store-Filterung im eigenen Provider, wodurch Landingpages eines Stores versehentlich in der Sitemap aller Stores auftauchen. Ein dritter Fallstrick betrifft vergessene Deaktivierungs-Logik, wenn eine Landingpage im Backend deaktiviert oder gelöscht wird, muss der nächste Sitemap-Lauf diese URL automatisch entfernen, sonst bleiben tote Links in der Sitemap stehen und werden von Suchmaschinen als Crawling-Fehler gewertet.

URL-Typ ItemProvider Priorität konfigurierbar? Multi-Store-fähig?
Produkte ProductItemProvider (Core) Ja, über XML Sitemap Konfiguration Ja, nativ
Kategorien CategoryItemProvider (Core) Ja, über XML Sitemap Konfiguration Ja, nativ
CMS-Seiten CmsPageItemProvider (Core) Ja, über XML Sitemap Konfiguration Ja, nativ
Custom-Landingpages Eigener Provider (dieser Artikel) Ja, über eigene system.xml-Felder Ja, mit expliziter Store-Filterung
Hreflang-Alternates Eigenes Plugin auf bestehenden Providern Nicht separat, folgt der Basis-URL Ja, zentral für die Kopplung der Sprach-Stores

Mironsoft

Magento-Entwicklung, Modul-Beratung und Systemarchitektur

Magento-Projekt, das eine zweite Meinung oder erfahrene Umsetzung braucht?

Wir entwickeln individuelle Magento-Module, beraten bei Architekturentscheidungen und übernehmen komplexe Umsetzungen, von der Service-Contract-Planung bis zum produktionsreifen Deployment.

Architektur-Beratung

Modul- und Systemarchitektur vor der Umsetzung fundiert durchdenken lassen.

Custom-Modul-Entwicklung

Individuelle Magento-Module nach Best Practices sauber umsetzen.

Code-Review & Audit

Bestehende Module auf Performance, Sicherheit und Wartbarkeit prüfen lassen.

10. Zusammenfassung

XML-Sitemap

Architektur

ItemProviderInterface-Implementierungen liefern Sitemap-Items, ItemProviderComposite führt alle registrierten Provider zusammen.

Erweiterung

Neue URL-Typen werden per di.xml im itemProviders-Array ergänzt, danach ist setup:di:compile zwingend erforderlich.

Konfiguration

Priorität und Änderungsfrequenz sollten über eigene system.xml-Felder konfigurierbar sein statt hart codiert zu werden.

Automatisierung

Ein zusätzlicher Cron-Job kann die generierte Sitemap automatisch über die Search-Console-API melden.

11. FAQ: XML-Sitemap

1Welches Interface muss ein eigener Sitemap-Provider implementieren?
ItemProviderInterface mit der Methode getItems, die eine Liste von SitemapItemInterface-Objekten für einen bestimmten Store zurückliefert.
2Reicht eine Änderung an der di.xml allein aus, damit ein neuer Provider berücksichtigt wird?
Nein, danach muss setup:di:compile laufen, teils sogar zweimal, sonst wird der neue Eintrag im itemProviders-Array stillschweigend ignoriert.
3Deckt die Standard-Sitemap auch eigene Content-Typen wie Landingpages ab?
Nein, der Standard deckt nur Produkte, Kategorien und CMS-Seiten ab, eigene Content-Typen benötigen einen eigenen ItemProvider.
4Woher sollten Priorität und Änderungsfrequenz eines eigenen Providers stammen?
Aus konfigurierbaren system.xml-Feldern statt aus hart codierten Werten, damit sich die Gewichtung ohne Code-Deploy anpassen lässt.
5Kann eine Sitemap-Datei für mehrere Stores gemeinsam genutzt werden?
Nicht sinnvoll, jeder Store mit eigener Sitemap benötigt einen eigenen Sitemap-Eintrag mit eigenem Dateinamen, sonst überschreiben sich generierte Dateien.
6Unterstützt Magento hreflang-Alternate-Links in der Sitemap nativ?
Nein, das muss über einen eigenen Provider oder ein Plugin ergänzt werden, das URLs anhand einer sprachunabhängigen Kennung zuordnet.
7Was passiert, wenn eine Sitemap die Grenze von fünfzigtausend URLs überschreitet?
Magento splittet automatisch in mehrere Dateien mit einem übergeordneten Sitemap-Index, ein eigener Provider kann diesen Splitpunkt früher auslösen als erwartet.
8Meldet Magento eine neue Sitemap automatisch an die Google Search Console?
Nein, das bleibt standardmäßig ein manueller Schritt, eine Automatisierung erfordert einen zusätzlichen Cron-Job mit Search-Console-API-Zugriff.
9Was passiert, wenn eine Landingpage deaktiviert wird?
Der nächste Sitemap-Lauf muss die URL automatisch entfernen, andernfalls bleiben tote Links stehen, die Suchmaschinen als Crawling-Fehler werten.
10Warum sollte ein eigener Provider store-bewusst filtern?
Ohne explizite Store-Filterung können Landingpages eines Stores fälschlich auch in der Sitemap eines anderen Stores auftauchen.