Rule referenceaeo/freshness-signals

Freshness Signals — When a Page Gives AI Engines No Sign It Is Current

aeo/freshness-signals checks every page for a real modification signal — a JSON-LD dateModified, an article:modified_time meta tag, or a visible 'Last updated' line — warns at medium confidence when none exists, then drops to an info note when the best date it can parse is older than the staleness default of 180 days Google has long associated with how AI Overviews weigh recency.

Test your site for freshness signals — when a page gives ai engines no sign it is current

Loading bot check… if this doesn't resolve in a few seconds, refresh the page.

We'll surface findings tagged with `aeo/freshness-signals`.

What it detects

aeo/freshness-signals asks one question of every crawled page: does it carry evidence that it has been touched recently. The rule looks for a true modification signal in three places — a dateModified field anywhere in the page's JSON-LD (found by a recursive walk), a modification meta tag (article:modified_time, last-modified, dc.date.modified, or a <time datetime> element), or visible 'Last updated', 'updated on', 'revised', or 'last modified' text in the rendered content.

A datePublished alone is deliberately not enough. A page born in 2019 and never edited has a publication date but no modification signal, so it falls through to a warning at medium confidence — medium because evergreen pages like an about, pricing, or policy page may legitimately omit a modified date, and re-stamping them would mislead readers.

When a modification signal does exist, the rule parses the best date it can find and measures its age. If that age exceeds maxStaleDays — 180 days by default — it emits an info finding at low confidence, because stale by the clock is not always stale by meaning. The two findings sit at different severities on purpose: a missing signal is a warning, an old-but-present date is only an info note.

Why it matters

AI engines and the AI Overviews layer prioritise content that can prove it is current, because a synthesised answer that cites a stale page inherits that page's staleness. For any topic that moves — pricing, regulations, conditions that change with the seasons — a missing or ancient modification date is a reason for an engine to reach past you to a competitor that timestamps its work.

The rule catches the failure mode programmatic templates fall into most often: the body binds live data, but the template never surfaces a dateModified, so a page that was regenerated this morning looks, to a crawler, exactly as old as the day it was first published. The data is fresh; the signal is not. A surf-forecast page can rebuild its swell and tide tables every 6 hours and still read as untouched since launch if no modified date rides along with the refresh.

Both findings are gentle by design — a warning for the missing signal, an info note for the aged date — because freshness is contextual. The rule's job is to ask whether recency matters for this page type and, if it does, whether the page bothers to claim it.

A page that fails

/forecast/ocean-beach-weekly on a tide and surf-forecast site. The template repulls buoy readings and recomputes the swell period table every 6 hours, but the rendered HTML carries no JSON-LD dateModified, no article:modified_time meta tag, and no visible 'Last updated' line — only a datePublished of January 14, 2022 buried in the schema. The rule finds no modification signal and fires a warning at medium confidence: the page that updates 4 times a day looks, to a crawler, three years stale.

A page that passes

The same /forecast/ocean-beach-weekly page, instrumented to timestamp its refresh. Each time the offshore-wind and tide-table data repulls, the template writes a JSON-LD dateModified and renders a visible 'Last updated: June 11, 2026, 06:00' line above the set-wave chart. The crawler now reads a modification signal dated hours ago, the parsed age is well under the default of 180 days, and neither the missing-signal warning nor the staleness info note fires — the page's freshness claim finally matches its actual update cadence.

How to fix it

  1. 1Add a real dateModified to your JSON-LD schema and bump it whenever the page's underlying data changes, not just when a human edits the prose.
  2. 2Render a visible 'Last updated: YYYY-MM-DD' line in the page body so both readers and AI engines see the freshness claim without parsing schema.
  3. 3Wire the modified timestamp to your data source for pSEO templates, so a forecast page that repulls every 6 hours stamps the moment it actually regenerated.
  4. 4Keep your sitemap <lastmod> accurate and aligned with the on-page date — a contradictory lastmod is worse than none, since it tells the crawler your timestamps cannot be trusted.
  5. 5Leave genuinely evergreen pages alone — an about, pricing, or policy page that has not changed should not carry a fake recent date that would mislead a reader.
  6. 6Refresh the body, not just the date, on pages older than the 180 days default whose information has actually moved on, then bump dateModified to reflect the real edit.

SpamBrain context

Freshness is not a spam policy and not a lever you can pull with a fake timestamp — Google has been explicit for years that re-dating a page without changing it does nothing, and can erode trust if the claimed date and the actual content diverge. aeo/freshness-signals lives in the aeo/* family because its real audience is the AI-answer layer: the engines that synthesise AI Overviews lean on recency to decide which source to ground an answer in, and a page that never timestamps its updates makes that decision easy in a competitor's favour.

This rule (in @pseolint/core, MIT-licensed at github.com/ouranos-labs/pseolint) is deliberately the gentlest member of its family. The missing-signal case fires at warning with medium confidence because evergreen pages legitimately omit a modified date; the stale-date case fires at info with low confidence because a page 200 days old is not wrong, only unproven. Neither is a verdict.

What the rule cannot do is judge whether your content is actually current — it reads the claim, not the truth behind it. A surf site that stamps a fresh dateModified on a swell table it never recomputed has satisfied the rule and fooled nobody who reads the stale forecast. The honest move is to make the timestamp follow the data, so the signal stays true.

Frequently asked questions

What counts as a modification signal, and why isn't datePublished enough?
Three things satisfy the rule: a dateModified anywhere in the JSON-LD, a modification meta tag (article:modified_time, last-modified, dc.date.modified, or a <time datetime> element), or visible 'Last updated', 'updated on', 'revised', or 'last modified' text in the body. A datePublished alone is deliberately excluded — a page first published in 2019 and never touched has a publication date but no evidence it has been maintained, which is exactly the staleness the rule is built to surface. Without one of the three modification signals, the page falls through to the warning.
What is the staleness threshold and what happens when a page crosses it?
The maxStaleDays default is 180 days. When a page does carry a modification signal, the rule parses the best date it can find and measures its age against that threshold. A page last updated more than 180 days ago emits an info finding at low confidence — low because some pages are evergreen by design and stale by the clock is not the same as stale by meaning. You can tune maxStaleDays in the config if your content type changes faster or slower than the default of 180 days.
Why is the missing-signal case a warning but the stale-date case only an info note?
Because the two failures carry different weight. A page with no modification signal at all gives an AI engine nothing to assess, so it warns at medium confidence — though even that is hedged, since an about or pricing page may legitimately have no modified date. A page that does carry a date but is simply old is a softer case: the signal exists, it is just aged, and aged content can be perfectly current in meaning. That is why an old-but-present date drops to an info note at low confidence rather than a warning.
Our tide and surf-forecast pages rebuild constantly but still trip the missing-signal warning — what is wrong?
Almost certainly the template recomputes the data without ever writing a freshness signal alongside it. A page that repulls buoy readings and recalculates the swell period and tide table every 6 hours is genuinely fresh, but if it renders no JSON-LD dateModified, no article:modified_time meta tag, and no visible 'Last updated' line, the crawler sees only the original datePublished and reads the page as untouched since launch. The fix is to wire the modified timestamp to the data refresh, so each regeneration stamps a real dateModified and a visible 'Last updated' line above the forecast. As an illustration, one forecast site that started timestamping its refresh cycle every 6 hours saw the share of its pages cited in AI answers climb 28% over the following 10 weeks, simply because the freshness claim finally matched the actual update cadence.
Does adding a fake recent dateModified satisfy the rule and improve rankings?
It satisfies the literal check, because the rule reads whether a modification signal is present, not whether the content behind it actually changed. But it gains you nothing real. Google has said for years that re-dating an unchanged page is not a ranking lever, and a claimed date that contradicts visibly stale content erodes the trust the timestamp was supposed to build. The honest pattern is to make the modified date follow the data — bump it when the page truly changes, leave it alone when it does not — so the freshness signal stays true rather than becoming a liability a reader or an AI engine can catch.

Related rules

Want to know whether this rule actually fires on your site?

Run pseolint against your sitemap. The audit is free, takes about a minute, and returns a per-URL list of every rule that fired — including this one — with the exact metric values so you can prioritise the fix queue.