Sitemap Analysis & Validation
Issue No: 3
Category: Crawlability & Indexation
Issue type: Issue, Warning, Suggestion
Priority: CRITICAL, IMPORTANT, STANDARD
Description
A sitemap is an XML file that lists a site's URLs and metadata to guide search engine crawlers; missing, malformed, or conflicting sitemaps can significantly reduce crawl efficiency and delay indexation of important pages.
How do we capture it
- Discover the sitemap URL:
- Check
robots.txtfor anySitemap:directives. - Try common paths:
/sitemap.xml,/sitemap_index.xml,/sitemap/,/sitemap.php,/sitemap.txt. - Check
<head>of every crawled HTML response for<link rel="sitemap" type="application/xml" href="...">and record thehrefvalue; flag pages where the tag is absent. Note: The crawler parses raw HTTP responses only and does not execute JavaScript to find these tags.
- Check
- Fetch the sitemap:
- Send HTTP GET request to the resolved sitemap URL(s).
- Record HTTP status code, response size, and
Last-Modified/Content-Typeheaders.
- Parse the XML:
- Detect type:
<sitemapindex>(index file) or<urlset>(standard sitemap). - For index files: extract all child
<sitemap><loc>entries and fetch each recursively. - For urlset files: extract all
<url>entries with<loc>,<lastmod>,<changefreq>,<priority>.
- Detect type:
- Validate each URL:
- Send HTTP GET requests to each
<loc>URL. - Record HTTP status code, redirect chains, and
Content-Type. - Cross-reference against robots.txt rules and HTTP/HTML
noindexdirectives.
- Send HTTP GET requests to each
- Cross-check:
- Compare sitemap URLs against robots.txt
Disallowrules.
- Compare sitemap URLs against robots.txt
- Validate canonical ↔
<loc>alignment:- For each crawled HTML response, extract the canonical URL from
<link rel="canonical" href="...">. - Look up the page's
<loc>entry in the parsed sitemap data. - Compare both values exactly: same scheme (
https), same domain, same path, same trailing-slash convention.
- For each crawled HTML response, extract the canonical URL from
- Detect image sitemap coverage:
- For each crawled page, collect all image URLs from raw
<img src>,<img srcset>,<meta property="og:image">, and JSON-LDimagefields. - Check whether those image URLs appear as
<image:image><image:loc>entries under the parent page's<url>block in the sitemap.
- For each crawled page, collect all image URLs from raw
- Detect video sitemap coverage:
- For each crawled page, detect embedded video via
<iframe>src patterns, HTML5<video>tags, and JSON-LDVideoObjectstructured data from the raw HTML text. - Check whether the page has a corresponding
<video:video>entry in the sitemap containing at minimum:<video:thumbnail_loc>,<video:title>, and<video:content_loc>/<video:player_loc>.
- For each crawled page, detect embedded video via
What to store
| Field | Type | Comment |
|---|---|---|
site_id | integer | Which site this sitemap belongs to |
type | text | Sitemap type: 'index' or 'urlset' |
slug | text | Identifier derived from filename, e.g. 'sitemap-products' |
url | text | Full absolute URL where this sitemap was fetched from |
url_count | integer | Total number of <url> entries parsed from this sitemap |
file_size | integer | File size in bytes of the raw fetched sitemap |
fetched_at | timestamp | Timestamp of when this sitemap was last fetched |
last_modified | timestamp | Value from HTTP Last-Modified response header |
generated_at | timestamp | Value from <lastmod> on the sitemap index entry |
http_status | integer | HTTP status code returned when fetching the sitemap file |
is_active | boolean | Whether this is the current active version for the site |
status | text | Processing state: 'pending', 'generated', 'failed' |
error | text | Error message if fetch or parse failed |
sitemap_id | integer | Which sitemap a URL belongs to |
loc | text | Absolute URL value from the <loc> tag |
lastmod | timestamp | Date from <lastmod> tag |
changefreq | text | Hint from <changefreq> |
priority | numeric | Page priority 0.0–1.0 from <priority> tag |
is_excluded | boolean | Whether this URL was excluded from the sitemap build |
exclude_reason | text | Reason for exclusion, e.g. 'noindex', 'redirected', 'blocked' |
is_indexable | boolean | Whether the URL is indexable (200 + no noindex + not disallowed) |
redirect_target | text | Final URL after following redirects |
canonical_url | text | Canonical URL declared on the page |
canonical_matches_loc | boolean | True if the canonical URL matches the <loc> value exactly |
has_head_sitemap_link | boolean | True if <link rel="sitemap" type="application/xml"> was found |
checked_at | timestamp | When this URL's HTTP status was last verified |
image_loc | text | <image:loc> — absolute URL of the image |
image_title | text | <image:title> — title of the image |
image_caption | text | <image:caption> — caption or description |
image_geo_location | text | <image:geo_location> — location string |
image_license | text | <image:license> — URL of the image licence |
thumbnail_loc | text | <video:thumbnail_loc> — URL of the video thumbnail |
title | text | <video:title> — video title |
description | text | <video:description> — short description |
content_loc | text | <video:content_loc> — URL of the actual video file |
player_loc | text | <video:player_loc> — URL of the video player embed |
duration | integer | <video:duration> — video length in seconds |
expiration_date | timestamp | <video:expiration_date> — when the video will expire |
rating | numeric | <video:rating> |
view_count | integer | <video:view_count> — total view count |
publication_date | timestamp | <video:publication_date> — when the video was published |
family_friendly | boolean | <video:family_friendly> — safe for all ages |
live | boolean | <video:live> — true if the video is a live stream |
engine | text | Target search engine submitted to |
submitted_at | timestamp | When the sitemap was submitted |
response_code | integer | HTTP response code from search engine API |
Condition for trigger
The following logic defines when to surface issues, warnings, or suggestions based on gathered telemetry.
-
Trigger Rule 1: Sitemap Unreachable
- Target Field:
http_status - Evaluation Logic:
http_status != 200 - Severity: CRITICAL
- Diagnostic Message: "Sitemap returned a non-200 HTTP status code. Search engines cannot process it."
- Target Field:
-
Trigger Rule 2: Complete Absence
- Target Field:
id - Evaluation Logic:
id IS NULL - Severity: CRITICAL
- Diagnostic Message: "No sitemap discovered at any known path or in robots.txt."
- Target Field:
-
Trigger Rule 3: Sitemap Size Over 50k URLs
- Target Field:
url_count - Evaluation Logic:
url_count > 50000 AND type = 'urlset' - Severity: WARNING
- Diagnostic Message: "Single sitemap exceeds 50,000 URLs without using a sitemap index."
- Target Field:
-
Trigger Rule 4: File Size Limit Exceeded
- Target Field:
file_size - Evaluation Logic:
file_size > 52428800 - Severity: WARNING
- Diagnostic Message: "Sitemap file exceeds the 50MB uncompressed size limit."
- Target Field:
-
Trigger Rule 5: High Error Rate
- Target Field:
http_status - Evaluation Logic:
COUNT(http_status != 200) / url_count > 0.10 - Severity: WARNING
- Diagnostic Message: "Over 10% of listed URLs are broken, redirected, or unavailable."
- Target Field:
-
Trigger Rule 6: Blocked by Robots.txt
- Target Field:
exclude_reason - Evaluation Logic:
exclude_reason = 'blocked' - Severity: WARNING
- Diagnostic Message: "A URL in the sitemap is blocked by a Disallow rule in robots.txt."
- Target Field:
-
Trigger Rule 7: Contains Noindex
- Target Field:
exclude_reason - Evaluation Logic:
exclude_reason = 'noindex' - Severity: WARNING
- Diagnostic Message: "A URL in the sitemap has a noindex meta tag or HTTP header."
- Target Field:
-
Trigger Rule 8: Not Declared in Robots.txt
- Target Field:
site_id - Evaluation Logic:
No directive = 'Sitemap' - Severity: SUGGESTION
- Diagnostic Message: "Sitemap is not declared in robots.txt."
- Target Field:
-
Trigger Rule 9: Missing Lastmod
- Target Field:
lastmod - Evaluation Logic:
lastmod IS NULL - Severity: SUGGESTION
- Diagnostic Message: "lastmod values are missing; search engines cannot prioritise correctly."
- Target Field:
-
Trigger Rule 10: Canonical Mismatch
- Target Field:
canonical_matches_loc - Evaluation Logic:
canonical_matches_loc = false - Severity: WARNING
- Diagnostic Message: "Canonical URL on page doesn't match its
<loc>entry exactly."
- Target Field:
Sources
- sitemaps.org — Official Protocol: https://sitemaps.org
- Google — Build and Submit a Sitemap: https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap
- Google — Sitemap Formats: https://developers.google.com/search/docs/crawling-indexing/sitemaps/sitemap-formats
- Google — Large Sitemaps & Sitemap Index Files: https://developers.google.com/search/docs/crawling-indexing/sitemaps/large-sitemaps
- Bing Webmaster — Sitemaps Guide: https://www.bing.com/webmasters/help/sitemaps-3b5cf6ed
- Google — Image Sitemaps: https://developers.google.com/search/docs/appearance/google-images#use-a-sitemap
- Google — Video Sitemaps: https://developers.google.com/search/docs/appearance/video#sitemap
- W3C — XML Specification: https://www.w3.org/XML/
Long description
A sitemap is a file — most commonly in XML format — that lists all URLs belonging to a website along with optional metadata about each URL. It acts as a structured roadmap that tells search engine crawlers which pages exist on a site, when they were last updated, how frequently they change, and how important they are relative to one another.
While crawlers can discover pages by following links, a sitemap ensures that even pages with few or no internal links — often called "orphan pages" — are still discovered and considered for indexing. This makes sitemaps especially valuable for large sites, newly launched sites, or sites with complex navigation.
Common scenarios flagged by the crawler:
- Sitemap returning 404 or 5xx: Wastes crawl budget and forces search engines to rely purely on links.
- Sitemap URLs blocked by robots.txt: Conflicting signal between sitemap inclusion and robots parsing.
- Sitemap exceeding limitations: Over 50,000 URLs or 50MB per sitemap will be ignored by Google.
- Canonical Mismatches: Trailing slashes or mismatched HTTP/HTTPS between
<loc>and<link rel="canonical">split signals.
How to Fix
- Ensure canonical status: The sitemap must be available via HTTP GET and return a
200 OKstatus without redirects. - Resolve contradictory signals: Remove URLs from the sitemap that are blocked by robots.txt or marked with
noindex. - Split large sitemaps: For files over 50,000 URLs or 50MB, split them into smaller chunks using a
<sitemapindex>. - Standardize
<loc>attributes: Ensure every URL in the sitemap exactly mirrors the canonical preference (e.g. enforcing HTTPS and consistent trailing slashes). - Declare dynamically: Place a
Sitemap:directive in the rootrobots.txtand keep the file dynamically generated to avoid serving stale values. - Integrate media entries: Map images and videos on critical URLs using XML
<image:image>and<video:video>namespaces.