Your article on “ChatGPT plugins” was published in 2023. You rewrote it last month — new screenshots, new model name, new examples — but the publishedAt field still says 2023-03-14. SERP shows “Mar 14, 2023” next to your title. Users skip it for fresher-looking competitors. Google’s freshness signal treats it as stale. The article was actually refreshed; the date just never moved.
The opposite trap is just as bad: bumping publishedAt to today on every cosmetic edit. Now every article looks freshly published, which Google reads as date-spam and discounts entirely. The right answer is two fields, a clear refresh policy, and a volatile flag for topics that legitimately need frequent updates.
Common causes
1. Only one date field — and it never moves
Your schema only has publishedAt. There is no updatedAt. So either the date never moves (article looks stale) or it gets bumped (article looks date-spammed). Both are wrong.
How to spot it: check your content collection schema for a separate updatedAt field. If missing, you have this problem by design.
2. updatedAt exists but authors don’t update it
The schema has updatedAt but authors forget to bump it on real refreshes. The page header shows “Updated: 2023-03-14” even though the article was rewritten last week.
How to spot it: compare git log mtime against frontmatter updatedAt. Articles where git mtime is 6+ months newer than updatedAt are stale-labeled.
3. Every cosmetic edit bumps the date
Author fixes a typo, bumps updatedAt to today. Author fixes another typo a week later, bumps again. The article hasn’t substantively changed in a year but updatedAt shows weekly motion. Google notices.
How to spot it: look at the git log -p for updatedAt changes. If 80% of bumps coincide with single-line diffs, you have date-spam.
4. Fast-moving topics not marked as volatile
Your article on “current ChatGPT pricing” inherently goes stale every quarter. Without a volatile: true flag, it lives in the same publishing rhythm as evergreen content and gets treated identically. It should refresh on a schedule.
How to spot it: list articles with keywords like “current,” “latest,” “2024,” “pricing,” “limits.” If they don’t have a volatile flag, they need one.
5. Schema markup uses wrong date
Your JSON-LD datePublished and dateModified are both pulled from publishedAt. Google’s freshness algorithm reads dateModified. If you have an updatedAt but JSON-LD doesn’t use it, the SERP date won’t reflect refreshes.
How to spot it: view-source on a refreshed article. Check the JSON-LD block:
curl -s https://site.com/en/articles/your-slug/ | grep -A2 'dateModified'
If dateModified matches old publish date, the schema is wrong.
Shortest path to fix
Step 1: Add a real updatedAt field
Update your content collection schema to require both:
// src/content/config.ts
import { z, defineCollection } from "astro:content";
const articles = defineCollection({
schema: z.object({
title: z.string(),
publishedAt: z.date(),
updatedAt: z.date().optional(),
volatile: z.boolean().default(false),
// ...
}),
});
publishedAt is set once at first publication and never changes. updatedAt is set on substantial refreshes (not typos).
Step 2: Wire JSON-LD and visible date to updatedAt
In your article layout, display whichever is most recent and emit it in JSON-LD dateModified:
---
const { article } = Astro.props;
const lastUpdate = article.data.updatedAt ?? article.data.publishedAt;
---
<time datetime={lastUpdate.toISOString()}>
Updated {lastUpdate.toLocaleDateString()}
</time>
<script type="application/ld+json" set:html={JSON.stringify({
"@context": "https://schema.org",
"@type": "Article",
datePublished: article.data.publishedAt.toISOString(),
dateModified: lastUpdate.toISOString(),
// ...
})} />
Now SERP shows the refresh date, and Google’s freshness algorithm sees real motion.
Step 3: Define a refresh policy
Document a rule for when updatedAt should bump:
- Substantial: new section added, model/pricing facts updated, code example rewritten, screenshot replaced — bump
- Cosmetic: typo fix, wording polish, internal link added — DO NOT bump
- Translation drift fix on counterpart only: DO NOT bump (the EN/ZH page being refreshed handles its own)
A reviewer enforces this on PRs. A bump on a cosmetic-only PR gets a “revert the date” comment.
Step 4: Mark volatile articles
For pricing, model lists, “current state of X” pieces, add volatile: true:
---
title: "Current ChatGPT Pricing"
volatile: true
updatedAt: 2026-05-24
---
A quarterly script flags any volatile: true article whose updatedAt is older than 90 days. That becomes your refresh queue.
# scripts/audit-volatile-staleness.mjs
import fs from "node:fs";
import matter from "gray-matter";
const THRESHOLD_DAYS = 90;
const now = Date.now();
for (const f of /* iterate articles */ []) {
const { data } = matter(fs.readFileSync(f, "utf8"));
if (!data.volatile) continue;
const last = new Date(data.updatedAt || data.publishedAt).getTime();
const ageDays = (now - last) / 86400000;
if (ageDays > THRESHOLD_DAYS) {
console.log(`STALE volatile: ${f} (${Math.round(ageDays)}d old)`);
}
}
Step 5: Backfill the existing stale labels
Run a one-time backfill:
1. List articles where git mtime is 6+ months newer than frontmatter publishedAt
2. For each, judge: did it actually get a substantive refresh?
3. If yes, set updatedAt to a reasonable recent date (the git mtime of the last big edit)
4. If no, leave it alone
Do NOT bulk-bump every article to today. Selective, defensible dates only.
Prevention
- Two-field schema:
publishedAtimmutable,updatedAtfor refreshes volatile: trueflag for time-sensitive topics; quarterly refresh queue- Refresh policy documented; reviewer enforces “substantial vs cosmetic” on PRs
- JSON-LD
dateModifieddriven fromupdatedAt, notpublishedAt - Audit script flags volatile articles older than 90 days
- One-time backfill of legitimately refreshed articles using git history as truth
Related
- Stale Articles Not Updated
- Content Site Sitemap Not Resubmitted After Big Changes
- Outdated Screenshots in Tutorials
- Content Site FAQ Schema Not Extracted
- Content Site Hreflang Tags Misconfigured
- Content Site Canonical Points to Self Wrong
Tags: #Content ops #Site quality #Site audit #Troubleshooting #publish-date