Validador de sitemap XML

Todas as regras do protocolo. Sem limite de URL nem cadastro.

Buscado direto da origem. Muitos servidores bloqueiam isso; se o seu bloquear, cole o conteúdo.
Entrada
AguardandoCole um documento para verificá-lo. A validação roda enquanto você digita.

Tudo roda nesta aba. Nada do que você colar é enviado, registrado ou transmitido para lugar nenhum. Abra o painel de rede e confira.

Cole seu sitemap acima, ou digite o endereço dele e clique em Buscar, e todas as regras do protocolo do sitemaps.org são verificadas de uma vez: a contagem de URLs contra o limite de 50.000, o tamanho descompactado contra o limite de 50 MB, e o formato de cada loc, lastmod, changefreq e priority. Arquivos de índice de sitemap têm o próprio conjunto de regras.

A maioria chega do Search Console segurando um "Não foi possível ler o sitemap" ou "O sitemap pode ser lido, mas contém erros", e nenhum dos dois aponta uma linha. Aqui cada achado cita o valor ao qual objetou, referencia a regra e diz o que escrever no lugar.

Duas coisas separam esta página do resto desta busca. Nada é enviado, então um sitemap de homologação listando páginas que você ainda não lançou fica na sua máquina. E verificações que não puderam rodar são reportadas como não executadas, não como aprovadas.

Os dois limites rígidos

Um único sitemap pode listar 50.000 URLs e pode ter 50 MB descompactado, o que o protocolo declara como 52.428.800 bytes. Os dois são verificados, e o tamanho em bytes é informado esteja você perto dele ou não, porque é o único número que não dá para julgar olhando o arquivo.

Gzip não compra folga: o limite se aplica depois da descompressão, então um sitemap.xml.gz de 3 MB que expande para 61 MB passou dele. Um índice de sitemaps carrega os mesmos dois limites. Ambos avisam em noventa por cento, já que um sitemap montado a partir de um catálogo que cresce cruza a linha numa terça-feira qualquer.

  • 50.000 entradas <url> por arquivo de sitemap.
  • 52.428.800 bytes descompactados, com ou sem gzip.
  • 2.048 caracteres por valor de <loc>.
  • 50.000 entradas <sitemap> por índice de sitemaps.

O namespace precisa ser exato

A raiz é <urlset>, ou <sitemapindex> para um índice, e ela precisa declarar http://www.sitemaps.org/schemas/sitemap/0.9. Três detalhes pegam as pessoas: é http e não https, mesmo num site https; não tem barra no fim; e a versão é 0.9 e sempre foi.

Erre isso e nada reclama alto. O arquivo continua bem formado e continua abrindo no navegador. Os buscadores simplesmente não reconhecem elementos no namespace errado, então leem o seu sitemap como um documento que não contém nada.

Misturar filhos <url> e <sitemap> é erro, porque um arquivo ou é um sitemap ou é um índice. A codificação declarada precisa ser UTF-8. De elementos de extensão só se verifica se têm um prefixo declarado; as regras de imagem, vídeo e notícias em si não são validadas aqui.

lastmod, changefreq e priority

lastmod precisa ser um W3C Datetime. AAAA-MM-DD vale sozinho; um carimbo de tempo completo vale se trouxer fuso, como em 2026-03-14T09:30:00+00:00. Um espaço no lugar do T, um timestamp Unix, 14/03/2026, ou um carimbo sem fuso são todos erros.

Uma segunda verificação de lastmod roda aqui, que nenhum outro validador gratuito faz. Se mais de quatro entradas trazem um e todos os valores são idênticos, você recebe um aviso: o seu gerador está carimbando a hora do build em vez da data em que o conteúdo mudou. O Google usa lastmod só quando ele é consistente e verificavelmente preciso, então esse padrão ensina o Google a ignorar o campo.

changefreq aceita sete valores em minúsculas: always, hourly, daily, weekly, monthly, yearly, never. priority precisa ficar entre 0.0 e 1.0. Os dois também são reportados como informação sempre que aparecem, porque o Google afirma na própria documentação que os ignora.

Mesmo host, mesmo diretório, e buscar por URL

Um sitemap em https://example.com/catalog/sitemap.xml pode listar URLs sob https://example.com/catalog/ e não pode listar URLs sob https://example.com/images/. Mesmo host, mesmo esquema, no próprio diretório ou abaixo dele. Essas regras precisam do endereço em que o sitemap será publicado, que o arquivo não contém.

É para isso que serve a caixa de URL, e você pode digitar um endereço sem buscar nada. Digite e três verificações ligam: outro host é erro, um esquema diferente é erro, e um loc fora do próprio diretório do sitemap é aviso. Deixe vazio e as três aparecem como não executadas, em vez de passarem caladas.

Buscar pede o arquivo do seu navegador direto para o seu servidor, sem proxy no meio, então o CORS bloqueia isso em muitas hospedagens. Quando bloqueia, você recebe o motivo e um comando curl para rodar você mesmo.

Verificando um sitemap em código

Vale plugar num passo de build, para que um sitemap que passou do limite de 50.000 URLs quebre o seu deploy em vez da sua indexação. Cada exemplo conta URLs, mede o tamanho descompactado e verifica 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'

As flags de segurança não são enfeite: Java, Python e C# todos resolvem entidades externas a menos que você diga o contrário, e um sitemap costuma ser um arquivo que não foi você quem escreveu, chegando pela rede a partir de um plugin. A flag --nonet do xmllint faz o mesmo serviço.

Perguntas frequentes

O Search Console diz que meu sitemap tem erros, mas esta ferramenta diz que está tudo bem. Por quê?

Os dois olham para coisas diferentes. Esta página verifica o arquivo: estrutura, namespace, limites, datas, e as regras de host e diretório assim que você fornece o endereço do sitemap. O Search Console verifica também o que só um rastreador pode, começando por se o arquivo estava acessível.

"Não foi possível buscar" normalmente significa um 404, uma cadeia de redirecionamentos, uma regra de robots.txt cobrindo o caminho do sitemap, ou uma página de erro HTML servida com status 200. Buscar a URL aqui mostra o código de status real. O outro descompasso comum é as URLs serem válidas mas as páginas por trás estarem com noindex ou terem sumido, o que é problema de rastreamento.

O Google usa changefreq e priority?

Não. O Google afirma na documentação de sitemaps que ignora os dois. Esta ferramenta reporta a presença deles como informação, e não como erro, porque são elementos legais e não fazem mal.

Na prática: nenhum arranjo de valores de priority vai fazer uma página ser rastreada antes de outra, e changefreq em hourly não vai aumentar a sua taxa de rastreamento. Se você mantém código que calcula isso, esse código não está valendo o esforço. lastmod é a exceção, e é o único elemento opcional que vale a pena acertar.

Que formato o lastmod precisa ter?

W3C Datetime, um subconjunto do ISO 8601. A forma válida mais simples é uma data pura, 2026-03-14. Se você incluir hora, precisa incluir fuso: 2026-03-14T09:30:00Z e 2026-03-14T09:30:00+00:00 estão ambos corretos.

As formas rejeitadas são justamente as que bancos de dados e templates produzem naturalmente. Um espaço no lugar do T é inválido, um carimbo sem fuso é inválido, e também um número de época Unix ou qualquer data com o dia primeiro, como 14/03/2026. Se as datas ruins vêm de um plugin e não do seu código, o valor citado no erro geralmente identifica qual.

Quantas URLs um sitemap pode ter?

50.000, e ele também precisa ficar abaixo de 52.428.800 bytes descompactado. O que você atingir primeiro é o que vale, e num site com URLs longas o limite de tamanho normalmente chega antes do de contagem.

Passando de qualquer um dos dois, divida o arquivo e liste as partes num índice de sitemaps: um arquivo pequeno no mesmo namespace cujos filhos são elementos <sitemap>, cada um com um <loc>. Um índice pode listar 50.000 sitemaps e está sujeito aos mesmos 50 MB. Envie o índice em vez de cada filho, e referencie-o no robots.txt.

Por que o botão Buscar falhou no meu sitemap?

Quase sempre CORS. O seu navegador não deixa esta página ler uma resposta de outro domínio a menos que aquele domínio envie um cabeçalho Access-Control-Allow-Origin, e poucos servidores enviam um para sitemap.xml. Isso é política do seu servidor, não um defeito do arquivo.

Não contornamos com um proxy, já que um proxy levaria a sua URL e o seu arquivo por um servidor que nós operamos. A mensagem de falha te dá um comando curl para o endereço que você digitou. Um sitemap com gzip precisa ser descompactado antes: curl -sL https://example.com/sitemap.xml.gz | gunzip.

Meu sitemap é enviado para algum lugar?

Não. O analisador e todas as regras são JavaScript rodando nesta aba. Não há back-end para onde mandar nada e nem analytics com acesso ao editor. Abra o painel de rede e valide um arquivo: os recursos da página carregam uma vez e depois nada.

Um sitemap publicado é público, mas o arquivo que você está depurando muitas vezes não é. Sitemaps de homologação listam páginas não lançadas, ferramentas internas e sites de clientes sob NDA, e vários dos verificadores bem posicionados nesta busca rodam no servidor. Sua entrada fica no localStorage deste navegador para que um recarregamento não a perca; Limpar remove.

Ferramentas relacionadas

Leitura complementar