Fastest fix: stop stamping lastmod with new Date() at build time and source it from a real content-change timestamp instead (frontmatter modifiedAt, falling back to publishedAt, or the last commit date for that file). If you have no reliable date for a URL, omit lastmod for that URL rather than fake it.
You deploy weekly. Your sitemap generator stamps lastmod with new Date().toISOString() at build time. From Google’s perspective, every page on your site changed yesterday, which means none of them did. That pattern is the textbook signature of a sitemap to ignore. Google’s own docs (as of June 2026) say it uses lastmod only “if it’s consistently and verifiably (for example by comparing to the last modification of the page) accurate,” and John Mueller has called setting today’s date on every entry “just lazy.” Once Google decides your lastmod is unreliable, it stops using it for the whole site, so the recrawl rate for genuinely updated pages drops. Worst case, “Discovered — currently not indexed” piles up. The fix is to derive lastmod from actual content change timestamps, not from build time.
Common causes
Ordered by hit rate.
1. lastmod set from new Date() at build time
The sitemap plugin or custom generator runs new Date().toISOString() once per build and uses that value for every entry. Build weekly and every URL “changed” weekly. This is the default behavior of several popular generators when no per-page date is wired in, including some @astrojs/sitemap, next-sitemap, and Gatsby setups, so check your config before assuming it is custom code.
How to spot it: Fetch the live sitemap, grep lastmod, and confirm all values are within seconds of each other and match your last deploy.
2. lastmod derived from file mtime, but a build step rewrites every file
A pre-build step (format, transform, minify) rewrites every content file in place, updating mtime. The generator reads mtime as lastmod. Every file looks freshly edited each build.
How to spot it: ls -lt src/content/ shows every file modified within the last build window, even ones you have not touched in months.
3. lastmod derived from CI job timestamp
CI checks out the repo with --depth=1 or in a Docker layer that resets mtimes. The generator falls back to “now” because git commit dates are not available.
How to spot it: All lastmod values fall within a 30-second window per deploy. Different deploys produce different uniform values.
4. lastmod correct in source but rewritten by CDN / edge worker
You generate the right lastmod from frontmatter, but an edge worker rewrites sitemap responses (adding cache-busting, normalizing format) and clobbers the value.
How to spot it: Compare the sitemap fetched from origin against the one fetched from the CDN. Differences in lastmod are the smoking gun.
5. lastmod rounded to the day, but every entry shares the same day
The generator floors timestamps to midnight UTC to “normalize.” If most edits happen in one window per deploy day, every entry collapses to the same date and Google treats it as suspicious.
How to spot it: All lastmod values are the same date, with 00:00:00Z time.
6. Newly added URLs inherit “now” instead of their actual publish date
A new article gets added to the sitemap with lastmod of today, even though it was published two years ago. The same logic incorrectly bumps every old URL on every backfill run.
How to spot it: Old archive URLs that have not changed in years show a recent lastmod.
What counts as a “significant” change
Before you wire up a new source for lastmod, get the definition right, because bumping the date on a trivial edit is what trains Google to distrust you in the first place. Google’s documentation (as of June 2026) draws the line like this:
Bump lastmod? | Change |
|---|---|
| Yes | Main content edited (paragraphs added, rewritten, removed) |
| Yes | On-page structured data (JSON-LD) changed |
| Yes | The links on the page changed meaningfully |
| No | Copyright year in the footer ticked over |
| No | Site-wide template, nav, or footer tweak |
| No | A typo fix or whitespace-only change |
| No | Auto-save or republish with no content delta |
Pick the same “meaningful edit” rule you use for dateModified in JSON-LD so the two never disagree.
Before you start
- Capture a snapshot of your live sitemap (or top sitemap index) before making changes. You will compare before and after.
- Look at Search Console → Sitemaps → report details for any “valid but warning” messages — Google will sometimes warn explicitly about lastmod reliability.
- Decide your source of truth for
lastmod: typically frontmattermodifiedAt(falling back topublishedAt). - If you have an existing audit of dates from Article Date and JSON-LD Date Mismatch, align with the same source.
Information to collect
- The current
lastmodvalue distribution: are most values within a 5-minute window, or spread across months? - The generator code or plugin that emits the sitemap.
- Frontmatter fields available per content type (
publishedAt,modifiedAt,updatedAt). - Recent Search Console “Crawl stats” trend — has total crawl request count dropped despite content additions?
- Whether your sitemap is split (one per content type) or a single monolith.
Step-by-step fix
Ordered by cost.
Step 1: Inspect the current sitemap
curl -s https://example.com/sitemap.xml | \
grep -oE '<lastmod>[^<]+</lastmod>' | sort | uniq -c | sort -rn | head -10
If the top entry covers thousands of URLs and matches your last deploy time, the bug is confirmed.
Step 2: Source lastmod from real content dates
Replace the build-time generator:
// before
{ loc: url, lastmod: new Date().toISOString() }
// after
{
loc: url,
lastmod: (article.modifiedAt ?? article.publishedAt),
}
Omit lastmod entirely rather than fake it if no real timestamp exists.
Emit the value in W3C Datetime format, which is what Google requires: either YYYY-MM-DD (for example 2026-06-04) or YYYY-MM-DDThh:mm:ss+TZD (for example 2026-06-04T17:15:30+00:00). If you include a time you must include a timezone offset, or Google reports an error in the Sitemaps report. Omitting the time defaults to midnight UTC. Date.prototype.toISOString() already produces a valid ...Z form, so the format itself is rarely the bug here — the source of the timestamp is.
Step 3: Backfill from git history when frontmatter is missing
git log --diff-filter=AM --follow --format=%aI -- "src/content/articles/en/${slug}.mdx" \
| head -1
That returns the most recent commit that added or modified the file. Use it as modifiedAt for articles without explicit dates.
Step 4: Stop pre-build steps from touching mtime
If a formatter rewrites every file every build, switch to in-memory transforms or guard with content hashing:
const before = await readFile(path);
const after = format(before);
if (after !== before) await writeFile(path, after);
writeFile only fires on real changes. Mtimes stop spuriously bumping.
Step 5: Verify CI preserves git timestamps
If you rely on commit dates, ensure CI clones with full history:
- uses: actions/checkout@v4
with:
fetch-depth: 0
Then read commit dates instead of mtime in the sitemap generator.
Step 6: Validate the edge layer is not rewriting
diff <(curl -s https://origin.example.com/sitemap.xml) \
<(curl -s https://example.com/sitemap.xml)
If lastmod differs, the CDN or edge worker is rewriting. Bypass or fix it.
Step 7: Resubmit and watch crawl response
In Search Console, resubmit the sitemap. Over 2-3 weeks watch the “Crawl stats” graph for a shift toward URLs that actually changed. The number of “Discovered — currently not indexed” should slowly decrease for genuinely updated pages.
Verify
- Sitemap
lastmoddistribution shows real variance over weeks and months, not a uniform value per deploy. - New articles get a
lastmodmatching theirpublishedAt, not today. - Search Console → Crawl stats shows recrawl prioritization for genuinely edited URLs.
- “Discovered — currently not indexed” trend reverses for important content.
- Your CMS edits flow into
lastmodwithin one deploy cycle.
Long-term prevention
- Document team-wide that
lastmodreflects real content change, never build time. - CI assertion: fail the build if more than 30% of sitemap entries share the same
lastmodminute. - Pair
lastmodsource with the same source that drivesdateModifiedin JSON-LD — one source of truth. - For static archive pages that genuinely never change, omit
lastmodrather than emit a stale value. - Audit edge / CDN response transformations quarterly to catch silent rewrites.
Common pitfalls
- Removing
lastmodentirely “to be safe.” Reallastmodvalues help Google prioritize. Only omit when you have no reliable source. - Bumping
lastmodon every page when you change navigation or the footer. Site-wide template changes are not per-page content changes. - Updating
lastmodto “now” when you fix a typo. Trivial edits should leavelastmodalone; align with the same “meaningful edit” rule you use fordateModified. - Splitting
lastmodanddateModifiedbetween two independent generators. They will drift; consolidate. - Resubmitting the sitemap five times in one day to “force a re-crawl.” That has no positive effect and can flag the sitemap.
FAQ
Q: Does Google actually penalize spammy lastmod patterns?
There is no ranking penalty, but there is a real cost. Google’s docs say it uses lastmod only when it is “consistently and verifiably accurate,” and John Mueller has said that setting today’s date on every entry “isn’t going to be something that works in favor of anyone, it’s just lazy.” Once Google decides your lastmod is unreliable, it ignores the field for the whole site, so you are treated as if you provided no signal at all. That costs you faster recrawls on the pages that genuinely changed.
Q: Should I include lastmod for every URL or only changed ones?
Include it whenever you have a reliable timestamp. Omit when you do not. An honest gap is better than a fabricated value.
Q: My CMS only tracks “edited date.” Is that enough?
Yes, as long as “edited date” only changes on real edits and not on auto-save or template changes. Verify by editing nothing and confirming “edited date” did not change.
Q: How fine-grained should lastmod be? Day, hour, second?
Match the precision of your tracking. Day-level (2026-05-24) is fine for content sites. Second-level is appropriate for fast-moving feeds. Do not invent precision you do not have. If you do emit a time, include a timezone offset or Google rejects it as a format error.
Q: How long until Google starts trusting lastmod again after I fix it?
There is no published switch-flip. In practice, once the values are accurate and stable, Google re-evaluates over the next few crawl cycles, typically a couple of weeks for an actively crawled site. Resubmitting the sitemap once after the fix is enough; do not resubmit repeatedly to try to speed it up.
Q: Do changefreq and priority still matter?
No. Google ignores both <changefreq> and <priority> entirely (as of June 2026) and pays attention to the URL and <lastmod>. Spend your effort on getting lastmod honest, not on tuning the other two.
Related
- Sitemap Pages Missing from Search Console
- Sitemap Submitted but Not Indexed
- Last-Modified Header Missing
- Article Date and JSON-LD Date Mismatch
- Robots Meta vs Sitemap Conflict
External references
- Build and submit a sitemap — Google Search Central — official
lastmodrules and the W3C Datetime format. - Sitemaps report — Search Console Help — where format errors and warnings surface.