squirrelscan
GitHub

AggregateRating on the wrong page: visible and on-topic

A sitewide star rating stamped on privacy, terms and blog pages, or one no reader can see. squirrel flags both as policy risks.

What rating scope is

A rating in structured data makes two claims: that this rating is about the thing this page is about, and that a reader can see the same rating in the page content. Google’s structured data guidelines require both. Markup a reader cannot verify, or markup that rates the site on a page that cannot be rated, is a policy violation rather than a validation error, and the risk is a manual action against the site’s rich results rather than a lost snippet on one page.

The usual cause is a template. A review badge added to a shared layout emits the same AggregateRating on every page, including the privacy policy, the terms page and every blog post. schema/review checks the shape of that markup and passes it, because the shape is fine. This rule asks the two questions the shape check cannot.

What squirrel checks

The rule walks the page’s flattened JSON-LD to a depth of 6. An aggregateRating property is attributed to the node that holds it, typed or not, and only the first object in an array-wrapped value is read. A standalone AggregateRating node is attributed to the nearest enclosing typed node. A page with no rating emits nothing. Otherwise it emits one or two checks:

  • rating-visible: passes with the message AggregateRating backed by a third-party review widget on the page when the page loads a known review platform such as Trustpilot, Yotpo, Judge.me or Feefo, or carries one of their placeholder containers. It passes with AggregateRating has a rating visible on the page when the page’s text carries a star glyph, a phrase like 4.9 / 5, 4.9 out of 5, 4.9 stars, rated 4.9 or 218 reviews, or the decimal rating value with either a dot or a comma as the separator. The text it searches is the extracted page content, the same content again with a space at every element boundary (so <h3>Customer rating</h3><p>5/5</p> is read as Customer rating 5/5, not Customer rating5/5), plus every alt, aria-label and title attribute. Otherwise it warns with <rating> on <entity> is not visible anywhere on <page> — a rating readers cannot see is a structured-data policy violation and risks a manual action, not just a lost rich result, where <rating> is AggregateRating 4.7 from 218 reviews, <entity> is the entity’s type, such as LocalBusiness, and <page> is this privacy policy page. The entity’s name is left out of the message, because it varies by page and would split one finding into several. It is still listed in the item labels.
  • rating-subject: emitted only on a page classified as a privacy policy, a terms page or an article. It passes with the message AggregateRating on this <page kind> page rates the page's own subject when nothing on the page is out of scope, where <page kind> is privacy policy, terms or article. Otherwise it warns with AggregateRating on <entity> is not the subject of this <page kind> page. Sitewide rating markup on a page that cannot be rated is a structured-data policy violation and risks a manual action, not just a lost rich result, where <entity> is again the entity’s type.

The rule is deliberately hard to trip. A review widget clears the visibility check outright, because a client-side badge is legitimately absent from crawled HTML. A whole-number rating such as 5 printed in the page text does not clear it on its own, since the digit is too common to be evidence; a decimal such as 4.7 does. On an article page only a carrier that represents the site itself is reported: every LocalBusiness subtype plus Organization, Corporation, OnlineBusiness, OnlineStore, NGO, EducationalOrganization, GovernmentOrganization, PerformingGroup, SportsOrganization, Brand, WebSite and WebPage. An article that reviews a Product keeps its rating. On privacy and terms pages every rating is reported, because those pages have no rateable subject at all. Entities listed per check are capped at 10, with a count of the rest, and the rule is skipped on a page detected as a soft 404.

An aggregateRating written as a node reference, {"@id": "…#rating"}, is read from the AggregateRating node with that @id anywhere in the page’s JSON-LD, and belongs to the entity that references it, so it is counted once. A relative @id resolves against the page URL. A reference to a node the page does not have is judged as written.

How to fix it

<div class="reviews">
  <p>Rated 4.7 out of 5 from 218 reviews</p>
</div>

Move the rating markup onto the pages whose subject it describes, which is the LocalBusiness on the homepage and location pages, or the Product on its own product page, and print the same numbers where a reader can see them.

The common false positive is a rating phrased in wording the visibility patterns do not cover, such as a rating rendered only as a bar chart with no numbers in text. The check errs towards passing, but a design with no readable rating anywhere is worth changing regardless of what the report says.

Rule IDschema/rating-scope
CategoryStructured Data
ScopePer-page
Severitywarning
Weight4/10

Enable / disable

Disable this rule

[rules]
disable = ["schema/rating-scope"]

Disable all Structured Data rules

[rules]
disable = ["schema/*"]

Enable only this rule

[rules]
enable = ["schema/rating-scope"]

enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.

Structured data findings ship in every audit next to the core SEO, performance and agent experience rules. See Fix SEO issues with AI for how an agent works through a report.

References

Check your site

Run squirrel audit https://example.com and open the Structured Data section of the report. Each flagged page names the rated entity and the page type. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close