/ Robots & Sitemap Fixes / Sitemap lastmod

How to Fix Sitemap lastmod and Priority

The lastmod field tells Google when content actually changed — it's the strongest re-crawl priority signal you have. Done right (reflecting real content modification timestamps), Google re-crawls updated pages within hours. Done wrong (every URL showing today because the file regenerates daily, or stuck in the past), Google learns to ignore your sitemap dates entirely. This guide covers correct ISO 8601 format, sourcing real modification dates, and why priority and changefreq are dead weight.

1. Audit current lastmod accuracy

Step 1
Check the values
curl -s https://example.com/sitemap.xml | \
  grep -E "<loc>|<lastmod>" | \
  head -40

# Spot-check 5-10 URLs. Open each URL in a browser.
# Does lastmod match when you last actually edited that content?
Step 2
Look for the all-same-date red flag
# Count unique lastmod values
curl -s https://example.com/sitemap.xml | \
  grep -oE '<lastmod>[^<]+</lastmod>' | \
  sort -u | head -10

# If you see one or two unique dates and they're recent — the file regenerates with current time on every URL

2. ISO 8601 format

Required format. Other formats parse incorrectly or get rejected:

<!-- Date only (recommended for most cases) -->
<lastmod>2024-01-15</lastmod>

<!-- Date with time and timezone (for high-frequency updates) -->
<lastmod>2024-01-15T14:30:00+00:00</lastmod>
<lastmod>2024-01-15T14:30:00Z</lastmod>

Common format mistakes

<!-- BAD: US format -->
<lastmod>01/15/2024</lastmod>

<!-- BAD: Unix timestamp -->
<lastmod>1705325400</lastmod>

<!-- BAD: no timezone with time component -->
<lastmod>2024-01-15T14:30:00</lastmod>

<!-- BAD: human format -->
<lastmod>January 15, 2024</lastmod>

3. Source from real content modification date

WordPress

Use post_modified (last edit date) not post_date (publish date):

// In a custom sitemap generator
$args = [
  'post_type' => ['page', 'post'],
  'post_status' => 'publish',
];
$query = new WP_Query($args);
while ($query->have_posts()) {
  $query->the_post();
  echo "<url>";
  echo "<loc>" . get_permalink() . "</loc>";
  echo "<lastmod>" . get_the_modified_date('Y-m-d') . "</lastmod>";
  echo "</url>";
}

// Yoast / Rank Math handle this correctly by default

Database-backed CMS

-- Source from updated_at column
SELECT
  slug,
  updated_at  -- NOT created_at
FROM pages
WHERE status = 'published'
ORDER BY updated_at DESC;

Static site generators

# Hugo
{{ range .Site.RegularPages }}
<url>
  <loc>{{ .Permalink }}</loc>
  <lastmod>{{ .Lastmod.Format "2006-01-02" }}</lastmod>
</url>
{{ end }}

# Hugo .Lastmod sources from front-matter or git history

# Next.js
export default async function sitemap() {
  const posts = await getPosts();
  return posts.map(post => ({
    url: `https://example.com/${post.slug}`,
    lastModified: post.updatedAt, // Date object or ISO string
  }));
}

Astro

// @astrojs/sitemap reads from page front-matter
// Set updatedDate in content collections schema
import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
  schema: z.object({
    title: z.string(),
    publishDate: z.date(),
    updatedDate: z.date().optional(),
  })
});
// Sitemap auto-uses updatedDate || publishDate

4. The all-same-date bug

⚠️ Common bug: sitemap generated by cron job sets lastmod to NOW for every URL. Google sees the sitemap claim everything updated today, crawls, sees nothing actually changed, learns to ignore your dates.

Bad pattern

// BAD: every URL gets the current timestamp
const sitemap = urls.map(url => ({
  loc: url,
  lastmod: new Date().toISOString()  // ← every URL gets "now"
}));

Right pattern

// RIGHT: lastmod from each URL's actual modification record
const sitemap = posts.map(post => ({
  loc: `https://example.com/${post.slug}`,
  lastmod: post.updatedAt.toISOString()  // ← from database
}));

5. Why priority and changefreq are dead

Google publicly stated (2017) it ignores both. Bing has similar policy. The fields were over-abused:

Strip from existing sitemaps

<!-- BEFORE -->
<url>
  <loc>https://example.com/about</loc>
  <lastmod>2024-01-15</lastmod>
  <changefreq>monthly</changefreq>
  <priority>0.8</priority>
</url>

<!-- AFTER -->
<url>
  <loc>https://example.com/about</loc>
  <lastmod>2024-01-15</lastmod>
</url>

Smaller files, less crawl bandwidth, same effect.

6. Special cases

Images and videos

<url>
  <loc>https://example.com/products/widget</loc>
  <lastmod>2024-01-15</lastmod>
  <image:image xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
    <image:loc>https://example.com/images/widget.jpg</image:loc>
    <image:caption>Blue ceramic widget</image:caption>
  </image:image>
</url>

News sitemaps (Google News)

<url xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
  <loc>https://example.com/news/article-1</loc>
  <news:news>
    <news:publication>
      <news:name>Example News</news:name>
      <news:language>en</news:language>
    </news:publication>
    <news:publication_date>2024-01-15T14:30:00+00:00</news:publication_date>
    <news:title>Article title</news:title>
  </news:news>
</url>
<!-- News sitemap entries auto-removed after 2 days -->

7. Common platform bugs

Bug: Yoast caching stale dates

Yoast caches sitemap output. If posts edit but cache hasn't expired, lastmod stays old. Force regeneration:

// functions.php
add_action('save_post', function($post_id) {
  // Trigger Yoast cache flush on every save
  if (function_exists('wpseo_xml_sitemaps_init')) {
    do_action('wpseo_ping_search_engines');
  }
});

Bug: WordPress core sitemap uses post_modified incorrectly

Default WordPress sitemap (since 5.5) uses post_modified which is correct. But if you've manually touched post_modified (via filter or update_post_meta) without actual content change, dates lie. Stick to letting WordPress manage it.

Bug: Static generator regenerates date on every build

# Hugo example - WRONG
{{ now.Format "2006-01-02" }}  <!-- always current build time -->

# RIGHT - source from front-matter
{{ .Lastmod.Format "2006-01-02" }}  <!-- from content file -->

8. Verify accuracy

Step 1
Compare to known edits
Pick 5 pages. Note when you last edited each. Check sitemap shows the matching lastmod. If consistently off, the source is wrong.
Step 2
Search Console correlation
Update a single page. Wait 1-2 days. Check Search Console URL Inspection on that URL. "Last crawled" should be near the lastmod. If Google ignores recent lastmod, you've over-claimed in the past and lost trust.
Step 3
Re-run Robots Tester
lastmod findings clear. Format ISO 8601, dates reflect actual content age, no priority/changefreq noise.
💡 The single rule: lastmod equals when the content was last edited, not when the file was last regenerated. Source from your CMS's modified_at column or equivalent. Don't strip priority/changefreq aggressively — just stop generating them in new code. Existing values do no harm.

🤖 Re-run the Robots & Sitemap Tester

Verify lastmod accuracy.

Run Tester →
Related Guides: Robots & Sitemap Fixes  ·  Fix Sitemap 404s  ·  Fix Sitemap Size  ·  Robots & Sitemap Guide
💬 Got a problem?