How to Fix Unupdated Sitemaps in Google Search Console: Improving with Last-Modified and 304 Responses

An issue occurred where the sitemap fetch date was not updating in Google Search Console (hereafter GSC), preventing new articles from being reflected in the index.
The site is operated on a WordPress + OpenLiteSpeed (LiteSpeed Cache plugin) environment. While the parent sitemap (sitemap.xml) was being fetched, monthly child sitemaps (e.g., sitemap-posttype-post.202508.xml) had not been updated since August 5th.

Since Bing Webmaster Tools was retrieving them normally, I deduced that the conditions related to Google’s crawling decisions and update detection were not being met.


目次

Objective

  • Ensure GSC reliably detects updates to child sitemaps

  • Keep the sitemap fetch date updated and promote the indexing of new pages

  • Prevent future erroneous caching and missed update detections


Issues Identified Through Investigation

$ curl -I https://donguri3.net/sitemap-posttype-post.202508.xml
HTTP/2 200
etag: "1924cd0be2d2a478d9675f0d7d647bd6"
content-type: application/xml; charset=UTF-8
x-robots-tag: noindex, follow
expires: Wed, 11 Jan 1984 05:00:00 GMT
cache-control: no-cache, must-revalidate, max-age=0, no-store, private, no-store
link: <https://donguri3.net/wp-json/>; rel="https://api.w.org/"
vary: Accept-Encoding
x-litespeed-cache: hit
date: Sun, 10 Aug 2025 13:40:14 GMT
server: LiteSpeed
alt-svc: h3=":443"; ma=2592000, h3-29=":443"; ma=2592000, h3-Q050=":443"; ma=2592000, h3-Q046=":443"; ma=2592000, h3-Q043=":443"; ma=2592000, quic=":443"; ma=2592000; v="43,46"
  1. Missing Last-Modified Header

    • Google uses Last-Modified as a reference for update detection, but it was not included in WordPress’s output.

  2. Potential ETag Fixation

    • Due to LiteSpeed caching and plugin effects, ETags may not change even when the content changes.

  3. Sitemaps Subject to LiteSpeed Cache

    • There was a risk that older XML would be returned due to cache hits.

  4. Lack of Support for If-Modified-Since

    • Even when Googlebot sends conditional requests, it returns 200 every time instead of 304 Not Modified.
      → Results in wasteful re-fetches, leading to inefficient crawling.


Solutions

1. Exclude Sitemaps from Caching

In LiteSpeed Cache (LSCWP), go to Cache > Exclude and add the following to “Do Not Cache URIs".

/sitemap.xml
/sitemap-*.xml
/sitemap-posttype-*.xml
/*sitemap*.xml

Additionally, added bypass rules to .htaccess or the OLS VHost Rewrite so that enabling caching in the future will not affect them.

# --- Completely bypass Sitemaps (prevent future erroneous caching) ---
RewriteCond %{REQUEST_URI} "(^/sitemap\.xml$|/sitemap-.*\.xml$|/.*sitemap.*\.xml$)"
RewriteRule .* - [E=cache-control:no-cache,E=cache-disable=1]

2. Implementing Last-Modified and 304 Responses

In functions.php, retrieve the last modified timestamp from the actual child sitemap data (the latest updated article of that month) and set it to Last-Modified.
Furthermore, compare it with If-Modified-Since, and if it is identical or newer, return 304 Not Modified.

add_action('init', function () {
$req = $_SERVER['REQUEST_URI'] ?? '';
if (!preg_match('#/(wp-sitemap|sitemap).*\.xml$#', $req)) return;

$lm_ts = null;
if (preg_match('#/sitemap-posttype-post\.(\d{4})(\d{2})\.xml$#', $req, $m)) {
$q = new WP_Query([
'post_type' => 'post',
'post_status' => 'publish',
'date_query' => [['year' => (int)$m[1], 'month' => (int)$m[2]]],
'orderby' => 'modified',
'order' => 'DESC',
'posts_per_page' => 1,
'no_found_rows' => true,
'fields' => 'ids',
]);
if (!is_wp_error($q) && !empty($q->posts)) {
$lm_ts = (int) get_post_modified_time('U', true, $q->posts[0]);
}
wp_reset_postdata();
}
if (!$lm_ts) $lm_ts = time() - 600;

$lm_http = gmdate('D, d M Y H:i:s', $lm_ts) . ' GMT';
header_remove('ETag');
header('Last-Modified: ' . $lm_http, true);
header('Cache-Control: no-cache, must-revalidate, max-age=0, no-store, private', true);

$ims_raw = $_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '';
$ims_ts = $ims_raw ? strtotime($ims_raw) : false;

if ($ims_ts !== false && $ims_ts >= $lm_ts) {
status_header(304);
http_response_code(304);
if (php_sapi_name() === 'cgi-fcgi') header('Status: 304 Not Modified', true);
header('Content-Length: 0');
exit;
}
}, 0);

3. Operation Verification

Header Check

curl -I https://blog.example.com/sitemap-posttype-post.202508.xml
  • Last-Modified matches the actual last modification time

  • No ETag

  • Cache-Control set to no-cache

304 Response Check

LM="Sat, 02 Aug 2025 06:05:23 GMT"
curl -I -H "If-Modified-Since: $LM" https://blog.example.com/sitemap-posttype-post.202508.xml

HTTP/2 304 Not Modified


Results

After the fix, I resubmitted the child sitemap whose fetching had stalled in GSC. After a short while, it displayed as “Success," and the last fetch date was updated.
Subsequently, new articles have been smoothly indexed as well.


Summary

  • The Last-Modified header is crucial for update detection

  • Cache exclusion settings are mandatory (especially in LiteSpeed environments)

  • 304 responses reduce wasteful crawls and improve update detection accuracy

  • When the “Last read" date in GSC does not update, review both server and application caching and header settings

With this response, Google’s crawling stabilized, and indexing speed improved.
If you are facing a similar issue where sitemaps are not updating in GSC, be sure to check your Last-Modified and cache control settings.