Most sitemap generators write a lastmod value for every URL on every build. Most of those values are wrong — not malformed, just meaningless. They record when the file was regenerated, not when the content changed. And once a site's lastmod values stop correlating with actual content changes, Google stops using them.
That matters because lastmod is the only element left in the sitemap spec that Google says it uses. <priority> and <changefreq> are ignored outright, which Google's own sitemap documentation states plainly. So a site with untrustworthy lastmod values has effectively published a list of URLs and nothing else.
What lastmod actually does
lastmod is a scheduling input, not an indexing instruction. Google's sitemap documentation says it uses the lastmod value if it is consistently and verifiably accurate — for example, by comparing it against the last modification Google itself observed for that page.
Read that carefully. It describes a verification loop:
- You claim a URL changed at time T.
- Googlebot eventually fetches the URL.
- Google compares the content it gets to the content it had.
- If the claim and the observation agree, often enough, the claims get weight in scheduling decisions.
Nothing in that loop promises a crawl, let alone a reindex. The realistic effect of an accurate lastmod is a shift in recrawl priority: changed pages move up the queue, unchanged pages drift down. On a 500-URL site that is invisible. On a 5-million-URL site where Googlebot can only refetch a fraction per day, it is the difference between updated content being picked up in hours versus weeks.
It is also a signal that can be withdrawn. The reasonable inference from the "consistently accurate" wording is that trust is evaluated at the site or sitemap level, not per URL — one honest URL in a sitemap full of build timestamps does not get special treatment.
The patterns that destroy lastmod trust
These are the failure modes that show up repeatedly in real sitemap audits:
- Build-time stamping. Every URL gets
<lastmod>equal to the deploy time. A weekly deploy means 100% of your URLs claim to have changed weekly. Google can verify that claim is false on the first few recrawls. - Boilerplate-triggered updates. The CMS updates the modified timestamp when a nav item, footer link, related-posts module, or ad slot changes. Google's documentation is explicit that
lastmodshould reflect the last significant content change, not trivial edits. - Metadata edits. Someone retags 40,000 products; every
lastmodjumps. The visible page body is byte-identical. - Current-time generation. Some sitemap endpoints render
lastmodasnow()at request time. Every fetch of the sitemap shows every URL as freshly modified. This is the most destructive variant. - Format errors.
lastmodmust be a W3C Datetime value.2024-13-02,02/11/2024, and timestamps with no timezone on a date-and-time value are parse failures, not soft signals. - Future dates. Scheduled-publishing systems sometimes emit timestamps ahead of the current time. Those are trivially falsifiable.
Three different dates, three different jobs
People conflate these constantly, and they are not interchangeable.
| Date | Where it lives | What it influences |
|---|---|---|
<lastmod> | XML sitemap | Recrawl scheduling priority |
Last-Modified | HTTP response header | Conditional requests (If-Modified-Since → 304) |
dateModified | Schema.org / on-page markup | Candidate input for the date shown in results |
They should agree, and on most CMS platforms they do not. A page can emit a Last-Modified header of today, a sitemap lastmod of last March, and a dateModified of the original publish date. Each of those is consumed by a different system for a different purpose, so an inconsistency does not break anything outright — but it does mean you cannot use one as evidence about another. If you care specifically about the date rendered in the SERP, that is a separate selection process from crawl scheduling.
How to measure your own lastmod accuracy
You can audit this without any Google data. You need two crawls spaced apart and a content hash that excludes boilerplate.
# per URL, per crawl
main = extract_main_content(html) # strip nav, footer, sidebars, ads
h = sha256(normalize_whitespace(main).encode()).hexdigest()
store(url, crawl_time, h, sitemap_lastmod)
Then, comparing crawl n to crawl n-1, bucket every URL:
- True positive — hash changed,
lastmodadvanced. - False positive — hash identical,
lastmodadvanced. This is the number that kills trust. - False negative — hash changed,
lastmodunchanged. This costs you discovery speed on real updates. - True negative — both unchanged.
Your headline metric is the false-positive rate: of all URLs whose lastmod moved, what share had no meaningful content change? If that is above a few percent, your lastmod is noise. Run it per sitemap file, because the problem is usually localized to one template — product pages, or paginated archives, or tag pages.
The normalization step is where this audit succeeds or fails. If your hash includes a rotating "customers also bought" block or a timestamp in the footer, every page will look changed and your audit will agree with your broken sitemap.
Confirming the effect in server logs
Once accuracy is fixed, you can test whether it is doing anything. Pick a set of pages you are about to genuinely update, record the publish time, and measure recrawl latency: the gap between the content change and the next Googlebot fetch of that URL in your logs.
Compare three cohorts over the same window:
- Pages updated with a correct
lastmodbump. - Pages updated without a
lastmodbump. - Pages not updated at all.
If lastmod is being used, the first cohort's median recrawl latency should be shorter than the second. Expect this to take weeks of data and expect high variance, because crawl scheduling responds to many inputs — internal link position, historical change rate, host-level crawl capacity. A single page recrawled fast proves nothing. A distribution shift across a few thousand URLs is evidence.
What to change
- Find out what your generator stamps. Fetch the sitemap twice, a few minutes apart, and diff it. If values moved, you are emitting generation time. Fix that first; nothing else matters until you do.
- Bind
lastmodto a content hash, not to a row'supdated_atcolumn. Store the hash of the significant content on save, and only advance the timestamp when it differs. - Exclude boilerplate from that hash deliberately. Decide which modules are content and which are furniture, and write it down. Related products are furniture. A rewritten spec table is content.
- Omit
lastmodrather than guess. A missing value costs you a scheduling hint. A wrong value costs you the hint plus the credibility of every other value in the file. - Split sitemaps by change behavior. Fast-changing templates in one file, archival content in another. It makes the accuracy audit per-file meaningful and limits the blast radius of one bad generator.
- Re-run the accuracy audit quarterly. A CMS upgrade or a new template can silently reintroduce build-time stamping, and nothing in Search Console will tell you.