XML-Sitemap-Validator

Alle Protokollregeln. Ohne URL-Limit, ohne Anmeldung.

Wird direkt von der Quelle abgerufen. Viele Server blockieren das; wenn Ihrer dazugehört, fügen Sie den Inhalt ein.
Eingabe
WartetFügen Sie ein Dokument ein. Die Prüfung läuft während der Eingabe.

Alles läuft in diesem Tab. Nichts, was Sie einfügen, wird hochgeladen, protokolliert oder irgendwohin gesendet. Öffnen Sie Ihr Netzwerkpanel und prüfen Sie es.

Fügen Sie Ihre Sitemap oben ein oder geben Sie ihre Adresse ein und drücken Sie Abrufen: Jede Regel des sitemaps.org-Protokolls wird auf einmal geprüft – die URL-Anzahl gegen das Limit von 50.000, die unkomprimierte Größe gegen das Limit von 50 MB und das Format jedes loc, lastmod, changefreq und priority. Sitemap-Indexdateien haben ihr eigenes Regelwerk.

Die meisten kommen aus der Search Console mit „Sitemap konnte nicht gelesen werden“ oder „Sitemap ist lesbar, enthält aber Fehler“, und keines von beiden nennt eine Zeile. Hier zitiert jeder Befund den beanstandeten Wert, nennt die Regel und sagt, was stattdessen dort stehen sollte.

Zwei Dinge trennen diese Seite vom Rest dieser Suche. Nichts wird hochgeladen, eine Staging-Sitemap mit noch nicht veröffentlichten Seiten bleibt also auf Ihrem Rechner. Und Prüfungen, die nicht laufen konnten, werden als nicht gelaufen gemeldet und nicht als bestanden.

Die zwei harten Grenzen

Eine einzelne Sitemap darf 50.000 URLs auflisten und unkomprimiert 50 MB groß sein, was das Protokoll als 52.428.800 Byte angibt. Beides wird geprüft, und die Bytegröße wird genannt, ob Sie nah dran sind oder nicht – sie ist die einzige Zahl, die sich beim Blick in die Datei nicht abschätzen lässt.

Gzip verschafft keinen Spielraum: Die Grenze gilt nach dem Entpacken, eine 3 MB große sitemap.xml.gz, die sich auf 61 MB ausdehnt, liegt also darüber. Ein Sitemap-Index trägt dieselben zwei Grenzen. Beide warnen bei neunzig Prozent, denn eine Sitemap aus einem wachsenden Katalog überschreitet die Linie an einem gewöhnlichen Dienstag.

  • 50.000 <url>-Einträge je Sitemap-Datei.
  • 52.428.800 Byte unkomprimiert, ob Sie gzippen oder nicht.
  • 2.048 Zeichen je <loc>-Wert.
  • 50.000 <sitemap>-Einträge je Sitemap-Index.

Der Namensraum muss exakt stimmen

Die Wurzel ist <urlset>, bei einem Index <sitemapindex>, und sie muss http://www.sitemaps.org/schemas/sitemap/0.9 deklarieren. Drei Details erwischen die Leute: Es ist http und nicht https, selbst auf einer https-Seite, es gibt keinen abschließenden Schrägstrich, und die Version ist 0.9 und war es immer.

Machen Sie es falsch, beschwert sich nichts laut. Die Datei bleibt wohlgeformt und öffnet sich weiterhin im Browser. Suchmaschinen erkennen Elemente im falschen Namensraum schlicht nicht und lesen Ihre Sitemap daher als ein Dokument, das nichts enthält.

<url>- und <sitemap>-Kinder zu mischen ist ein Fehler, denn eine Datei ist entweder eine Sitemap oder ein Index. Die deklarierte Kodierung muss UTF-8 sein. Bei Erweiterungselementen wird nur geprüft, ob sie ein deklariertes Präfix haben; die Regeln für Bild, Video und News selbst werden hier nicht validiert.

lastmod, changefreq und priority

lastmod muss ein W3C Datetime sein. JJJJ-MM-TT ist für sich gültig; ein vollständiger Zeitstempel ist gültig, wenn er eine Zone trägt, etwa 2026-03-14T09:30:00+00:00. Ein Leerzeichen statt des T, ein Unix-Zeitstempel, 14.03.2026 im Tag-zuerst-Format oder ein Zeitstempel ohne Zone sind allesamt Fehler.

Eine zweite lastmod-Prüfung läuft, die kein anderer kostenloser Validator macht. Tragen mehr als vier Einträge ein lastmod und sind alle Werte identisch, gibt es eine Warnung: Ihr Generator stempelt die Build-Zeit statt des Datums, an dem sich der Inhalt geändert hat. Google verwendet lastmod nur, wenn es durchgängig und nachprüfbar korrekt ist – dieses Muster bringt Google also bei, das Feld zu ignorieren.

changefreq akzeptiert sieben Kleinbuchstaben-Werte: always, hourly, daily, weekly, monthly, yearly, never. priority muss zwischen 0.0 und 1.0 liegen. Beide werden außerdem als Information gemeldet, sobald sie überhaupt vorkommen, denn Google schreibt in der eigenen Dokumentation, dass es sie ignoriert.

Gleicher Host, gleiches Verzeichnis, und Abrufen per URL

Eine Sitemap unter https://example.com/catalog/sitemap.xml darf URLs unterhalb von https://example.com/catalog/ auflisten und darf keine URLs unterhalb von https://example.com/images/ auflisten. Gleicher Host, gleiches Schema, im eigenen Verzeichnis oder darunter. Diese Regeln brauchen die Adresse, unter der die Sitemap veröffentlicht wird, und die steht nicht in der Datei.

Dafür ist das URL-Feld da, und Sie dürfen eine Adresse eintippen, ohne abzurufen. Tun Sie das, schalten sich drei Prüfungen ein: ein anderer Host ist ein Fehler, ein anderes Schema ist ein Fehler, und ein loc außerhalb des eigenen Verzeichnisses der Sitemap ist eine Warnung. Lassen Sie es leer, erscheinen alle drei als nicht gelaufen, statt still durchzugehen.

Abrufen fordert die Datei aus Ihrem Browser direkt bei Ihrem Server an, ohne Proxy dazwischen, weshalb CORS es bei vielen Hostern blockiert. Wenn das passiert, bekommen Sie den Grund und einen curl-Befehl zum Selbstausführen.

Eine Sitemap in Code prüfen

Lohnt sich in einem Build-Schritt, damit eine Sitemap, die über das Limit von 50.000 URLs gewachsen ist, Ihr Deployment scheitern lässt statt Ihrer Indexierung. Jedes Beispiel zählt URLs, misst die unkomprimierte Größe und prüft lastmod.

// Node 18 or later.  npm i @xmldom/xmldom
import { DOMParser } from '@xmldom/xmldom';

const SITEMAP_NS = 'http://www.sitemaps.org/schemas/sitemap/0.9';
const MAX_URLS = 50_000;
const MAX_BYTES = 52_428_800;

const response = await fetch('https://example.com/sitemap.xml');
const xml = await response.text();
const bytes = Buffer.byteLength(xml, 'utf8');

// xmldom does not resolve external entities, so there is no XXE surface here.
const doc = new DOMParser().parseFromString(xml, 'text/xml');
const root = doc.documentElement;

if (root.namespaceURI !== SITEMAP_NS) {
  throw new Error('Namespace is "' + root.namespaceURI + '", expected "' + SITEMAP_NS + '"');
}

const locs = doc.getElementsByTagNameNS(SITEMAP_NS, 'loc');
console.log(locs.length + ' URLs, ' + bytes + ' bytes uncompressed');
if (locs.length > MAX_URLS) throw new Error('Over the 50,000 URL limit');
if (bytes > MAX_BYTES) throw new Error('Over the 50 MB limit');

const lastmods = doc.getElementsByTagNameNS(SITEMAP_NS, 'lastmod');
for (let i = 0; i < lastmods.length; i++) {
  const v = lastmods.item(i).textContent.trim();
  // W3C Datetime: a bare date, or a timestamp that carries a zone.
  const ok = /^\d{4}-\d{2}-\d{2}$/.test(v)
    || (/^\d{4}-\d{2}-\d{2}T/.test(v) && /(Z|[+-]\d{2}:\d{2})$/.test(v));
  if (!ok) console.error('Invalid lastmod: ' + v);
}
# pip install lxml requests
import re
import sys
import requests
from lxml import etree

SITEMAP_NS = 'http://www.sitemaps.org/schemas/sitemap/0.9'
MAX_URLS, MAX_BYTES = 50_000, 52_428_800

raw = requests.get('https://example.com/sitemap.xml', timeout=30).content

# The three flags are the safe form. lxml will otherwise expand entities and
# fetch a DTD the document references.
parser = etree.XMLParser(resolve_entities=False, no_network=True, load_dtd=False)
root = etree.fromstring(raw, parser)

if etree.QName(root).namespace != SITEMAP_NS:
    sys.exit('Wrong namespace: %s' % etree.QName(root).namespace)

locs = root.findall('{%s}url/{%s}loc' % (SITEMAP_NS, SITEMAP_NS))
print('%d URLs, %d bytes uncompressed' % (len(locs), len(raw)))
if len(locs) > MAX_URLS:
    sys.exit('Over the 50,000 URL limit')
if len(raw) > MAX_BYTES:
    sys.exit('Over the 50 MB limit')

W3C = re.compile(r'^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2}))?$')
for lm in root.iter('{%s}lastmod' % SITEMAP_NS):
    if not W3C.match((lm.text or '').strip()):
        print('Invalid lastmod:', lm.text)

# The protocol publishes an XSD, which lxml can check structure against:
#   schema = etree.XMLSchema(etree.parse('sitemap.xsd'))
#   schema.assertValid(root.getroottree())
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.format.DateTimeParseException;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

final String NS = "http://www.sitemaps.org/schemas/sitemap/0.9";

byte[] bytes = HttpClient.newHttpClient()
    .send(HttpRequest.newBuilder(URI.create("https://example.com/sitemap.xml")).build(),
          HttpResponse.BodyHandlers.ofByteArray())
    .body();

DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
// Without setNamespaceAware(true) every namespace lookup below returns nothing.
// This is the single most common reason sitemap parsing code silently finds zero URLs.
f.setNamespaceAware(true);
f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
f.setXIncludeAware(false);

Document doc = f.newDocumentBuilder().parse(new ByteArrayInputStream(bytes));
NodeList locs = doc.getElementsByTagNameNS(NS, "loc");

System.out.printf("%d URLs, %d bytes uncompressed%n", locs.getLength(), bytes.length);
if (locs.getLength() > 50_000) System.err.println("Over the 50,000 URL limit");
if (bytes.length > 52_428_800) System.err.println("Over the 50 MB limit");

NodeList lastmods = doc.getElementsByTagNameNS(NS, "lastmod");
for (int i = 0; i < lastmods.getLength(); i++) {
    String v = lastmods.item(i).getTextContent().trim();
    try {
        if (v.length() == 10) LocalDate.parse(v);
        else OffsetDateTime.parse(v);   // rejects a timestamp with no zone
    } catch (DateTimeParseException e) {
        System.err.println("Invalid lastmod: " + v);
    }
}
using System.Globalization;
using System.Xml;

const string Ns = "http://www.sitemaps.org/schemas/sitemap/0.9";

// XmlReader streams, so a 50 MB sitemap never becomes a 400 MB DOM.
var settings = new XmlReaderSettings
{
    DtdProcessing = DtdProcessing.Prohibit,  // external entities are never fetched
    XmlResolver = null,
    IgnoreWhitespace = true,
};

string[] w3c =
{
    "yyyy-MM-dd",
    "yyyy-MM-ddTHH:mmK",
    "yyyy-MM-ddTHH:mm:ssK",
    "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
};

byte[] bytes = await new HttpClient().GetByteArrayAsync("https://example.com/sitemap.xml");

int urls = 0;
using var stream = new MemoryStream(bytes);
using var reader = XmlReader.Create(stream, settings);
while (reader.Read())
{
    if (reader.NodeType != XmlNodeType.Element || reader.NamespaceURI != Ns) continue;

    if (reader.LocalName == "loc")
    {
        urls++;
    }
    else if (reader.LocalName == "lastmod")
    {
        var v = reader.ReadElementContentAsString().Trim();
        // TryParse would accept 03/14/2026. TryParseExact against the four
        // permitted layouts is what the protocol actually asks for.
        if (!DateTimeOffset.TryParseExact(v, w3c, CultureInfo.InvariantCulture,
                                          DateTimeStyles.None, out _))
        {
            Console.Error.WriteLine("Invalid lastmod: " + v);
        }
    }
}

Console.WriteLine(urls + " URLs, " + bytes.Length + " bytes uncompressed");
if (urls > 50_000) Console.Error.WriteLine("Over the 50,000 URL limit");
if (bytes.Length > 52_428_800) Console.Error.WriteLine("Over the 50 MB limit");
# The protocol publishes an XSD, so xmllint can check the structure offline.
curl -sL https://example.com/sitemap.xml -o sitemap.xml
curl -sO https://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd

xmllint --noout --nonet --schema sitemap.xsd sitemap.xml

# The two hard limits:
xmllint --nonet --xpath 'count(//*[local-name()="loc"])' sitemap.xml   # cap 50,000
wc -c < sitemap.xml                                                    # cap 52,428,800

# A gzipped sitemap must be decompressed before either check:
curl -sL https://example.com/sitemap.xml.gz | gunzip > sitemap.xml

# Pull every URL out, one per line, for a crawler or a spreadsheet:
xmllint --nonet --xpath '//*[local-name()="loc"]/text()' sitemap.xml | tr -s '\n'

Die Sicherheitsflags sind keine Deko: Java, Python und C# lösen externe Entities auf, sofern man es ihnen nicht abgewöhnt, und eine Sitemap ist meist eine Datei, die Sie nicht geschrieben haben und die über ein Netzwerk aus einem Plugin kommt. Das Flag --nonet von xmllint leistet dasselbe.

Häufige Fragen

Die Search Console meldet Fehler in meiner Sitemap, dieses Werkzeug sagt, sie sei in Ordnung. Warum?

Beide schauen auf Verschiedenes. Diese Seite prüft die Datei: Struktur, Namensraum, Grenzen, Datumsangaben und – sobald Sie die Sitemap-Adresse angeben – die Host- und Verzeichnisregeln. Die Search Console prüft zusätzlich, was nur ein Crawler sehen kann, angefangen damit, ob die Datei überhaupt erreichbar war.

„Konnte nicht abgerufen werden“ heißt meist 404, eine Weiterleitungskette, eine robots.txt-Regel, die den Sitemap-Pfad abdeckt, oder eine HTML-Fehlerseite mit Status 200. Die URL hier abzurufen zeigt den echten Statuscode. Die andere häufige Abweichung: Die URLs sind gültig, aber die Seiten dahinter sind auf noindex gesetzt oder verschwunden – das ist ein Crawling-Problem.

Nutzt Google changefreq und priority?

Nein. Google schreibt in seiner Sitemap-Dokumentation, dass es beide ignoriert. Dieses Werkzeug meldet ihr Vorhandensein als Information und nicht als Fehler, denn es sind zulässige Elemente und sie schaden nicht.

Praktisch: Keine Anordnung von priority-Werten sorgt dafür, dass eine Seite vor einer anderen gecrawlt wird, und ein auf hourly gesetztes changefreq erhöht Ihre Crawl-Rate nicht. Wenn Sie Code pflegen, der beides berechnet, verdient dieser Code sein Gehalt nicht. lastmod ist die Ausnahme und das einzige optionale Element, das sich richtig zu machen lohnt.

Welches Format braucht lastmod?

W3C Datetime, eine Teilmenge von ISO 8601. Die einfachste gültige Form ist ein reines Datum, 2026-03-14. Wenn Sie eine Uhrzeit angeben, müssen Sie eine Zone angeben: 2026-03-14T09:30:00Z und 2026-03-14T09:30:00+00:00 sind beide korrekt.

Die abgelehnten Formen sind genau die, die Datenbanken und Templates von sich aus erzeugen. Ein Leerzeichen statt des T ist ungültig, ein Zeitstempel ohne Zone ist ungültig, und ebenso eine Unix-Epoch-Zahl oder jedes Datum mit dem Tag zuerst wie 14/03/2026. Kommen die schlechten Datumsangaben aus einem Plugin statt aus Ihrem eigenen Code, verrät der im Fehler zitierte Wert meist, aus welchem.

Wie viele URLs darf eine Sitemap haben?

50.000, und sie muss außerdem unter 52.428.800 Byte unkomprimiert bleiben. Was Sie zuerst erreichen, zählt, und auf einer Seite mit langen URLs kommt die Größengrenze meist vor der Anzahl.

Jenseits von beidem teilen Sie die Datei und listen die Teile in einem Sitemap-Index: eine kleine Datei im selben Namensraum, deren Kinder <sitemap>-Elemente mit je einem <loc> sind. Ein Index darf 50.000 Sitemaps auflisten und unterliegt denselben 50 MB. Reichen Sie den Index ein statt jedes Kind, und verweisen Sie in der robots.txt darauf.

Warum ist die Schaltfläche Abrufen bei meiner Sitemap fehlgeschlagen?

Fast immer CORS. Ihr Browser lässt diese Seite eine Antwort von einer anderen Domain nur lesen, wenn diese Domain einen Access-Control-Allow-Origin-Header schickt, und für sitemap.xml schicken das die wenigsten Server. Das ist die Richtlinie Ihres Servers, kein Fehler in der Datei.

Wir umgehen das nicht mit einem Proxy, denn ein Proxy würde Ihre URL und Ihre Datei durch einen Server tragen, den wir betreiben. Die Fehlermeldung gibt Ihnen einen curl-Befehl für die eingegebene Adresse. Eine gzippte Sitemap muss zuerst entpackt werden: curl -sL https://example.com/sitemap.xml.gz | gunzip.

Wird meine Sitemap irgendwohin hochgeladen?

Nein. Der Parser und jede Regel sind JavaScript, das in diesem Tab läuft. Es gibt kein Backend, an das sich etwas senden ließe, und keine Analytics mit Zugriff auf den Editor. Öffnen Sie Ihr Netzwerkpanel und validieren Sie eine Datei: Die Seiten-Assets laden einmal, danach nichts mehr.

Eine veröffentlichte Sitemap ist öffentlich, die Datei, die Sie gerade debuggen, oft nicht. Staging-Sitemaps listen unveröffentlichte Seiten, interne Werkzeuge und Kundenseiten unter Geheimhaltung, und mehrere der Prüfer, die für diese Suche ranken, laufen serverseitig. Ihre Eingabe liegt im localStorage dieses Browsers, damit ein Neuladen sie nicht verliert; „Leeren“ entfernt sie.

Verwandte Werkzeuge

Zum Weiterlesen