The deploy finished clean, and the sitemap still lied. Every page listed, whether we had touched it recently or not at all, carried the exact same last-changed date. No warning, no failed check, nothing red anywhere in the log. Just a file full of dates that were all wrong in the identical, suspicious way.
A host that builds from an extracted snapshot has no git history to read. A build step that asks git for a page's last change gets nothing back, so every lastmod collapses onto the deploy moment, with no error anywhere.
Google says it only trusts a lastmod value when it stays consistently and verifiably accurate. A date that repeats on every page cannot clear that bar.
The short answer
- A build host that only works from an extracted code snapshot has no git history to read, so a real last-changed lookup returns nothing.
- Google only trusts a lastmod date when it stays consistently and verifiably accurate, and a value that never changes fails that test.
- The fix is a small file, committed with the code, that carries the real dates in, so the build never has to go looking for them.
Why it looked fine on our own machines
It passed every local test, because a local machine carries the full history of every commit ever made to a project. Run the same build step there and it finds whatever date it asks for without complaint. The failure only shows up once the code leaves for a host that never receives that history, working instead from a single extracted copy of the latest commit. Local success only proves the step works where a full git checkout exists.
Astro, the framework behind this site, ships its own recipe for exactly this problem, and it uses the same approach: read the date of the last commit that touched a file. Astro says so itself, plainly, in that same recipe: the approach is unreliable on a host that only holds a shallow copy of the repository. The caveat was in the recipe the whole time. A caveat sitting in a framework's own documentation is not a warning meant for someone else's project; it is a warning meant for the exact build we were writing, and we found it in the wrong order.
The part that stings is how quiet the failure stayed. A broken build fails loudly. A wrong lastmod just sits there, technically valid XML, technically a date, doing nothing useful for months until someone notices every page shares a birthday. That is the kind of failure worth watching for anywhere a build depends on something the deploy host cannot see.
Astro The framework behind this site and every client site we build.The three deploys it took to fix it properly
Each one taught us something the last one had gotten wrong, and each one looked correct right up until the sitemap shipped and proved otherwise.
- First deploy
The git-log approach shipped
It read every date correctly on our own machines, so it went out. The sitemap came back with a single date, repeated across every page.
- Second deploy
A quieter kind of wrong
A patch went out that still derived the dates at build time, and the sitemap came back wrong again.
- Third deploy
The manifest that held
We replaced the whole approach: a small file, generated locally where the real git history exists, committed alongside the content it describes, and simply read back at build time.
How to check your own site in a minute
Open the raw sitemap file in a browser tab and read the lastmod column straight down the page. This works on any platform, not only one built the way ours is: the tell is the same everywhere, because the underlying mistake is the same everywhere.
Checking your own sitemap for this
-
Open the raw sitemap file
Load it directly in the browser, the way a crawler would, not through any dashboard that might reformat it.
-
Scan the lastmod values
Every URL entry carries a lastmod date next to it. Look for pages you know have gone untouched for a long stretch.
-
Compare a couple of unrelated pages
Pick pages built months apart. Two unrelated pages with the identical date are showing you the deploy date, not their own.
What changed on our own site
The file we run now is small: one record per page, holding the last real date its content changed, filled in at the moment the change was made. The build step that turns pages into a sitemap reads that file directly and stops there. It never has to reconstruct a history it was never given, because the history got written down before the build ever started.
That ordering is the real fix, more than any particular file format is. A build step should read a fact that was recorded when something true was known, not attempt to derive that fact later, in an environment that may not have the evidence anymore. The same shape shows up anywhere a build pulls from a source that only exists on one machine: a local database, a developer's own clock, a file that lives outside the repository entirely. The moment that source is missing on the host doing the build, whatever depended on it goes quietly wrong instead of loudly failing. A sitemap is just the place this particular version of the mistake happens to surface first.
A build step that works on a local machine proves it works somewhere with full git history. It proves nothing about the host that will build the site for real.
It is the same discipline behind our own technical SEO work generally: the parts of a page that decide whether a crawler trusts what it is reading get built to be checked, not assumed. A business that updates its hours, its service area or its pricing regularly needs that update to read as recent, not as one more line in a file that has claimed the same date for every page for months.
This post was drafted the way every post on this blog is: from our own working notes, checked against what we could verify, following the process on our editorial standards page.
The next time a build finishes green, open the sitemap it produced instead of trusting the deploy log that says everything passed. If every date on it matches, that is not proof nothing changed. It is proof the one field meant to tell a crawler what changed has stopped saying anything at all, and it has probably been saying nothing for longer than anyone noticed.
Talk to Offer before you decide anything.
You'll talk with Offer, not a sales rep. A short call, no pitch deck.
Talk to OfferThis post was drafted with AI tools from our own project records, anonymized, and checked by us before it published. Read our editorial standards.