Structured data is the part of a site nobody looks at, which is exactly why it rots. It is invisible in the browser, it does not break the build when it is wrong, and it is usually written once and never read again.
We audited our own site recently and found all three of the classic failures. This is what to look for in yours.
1. Invented Values
Start here, because this one carries real risk.
Our homepage emitted an AggregateRating of 5.0 across nine reviews, with every individual testimonial marked ratingValue: "5". The testimonial data contained no rating field at all — nine names, job titles and quotes, and nothing numeric anywhere.
Those numbers had been generated in the schema layer. Nobody had rated anything.
Two separate problems:
- The values were untrue. That is disqualifying on its own.
- Self-serving review markup is a policy violation. Google's structured data guidelines are explicit that an organisation marking up reviews about itself, on its own site, is ineligible for rich results — and it is treated as a spam-policy issue, not a missed opportunity.
The fix is deletion, not correction. The testimonials still appear on the page as ordinary content, which is where social proof belongs.
A good rule: if a number appears in your JSON-LD, you should be able to point at the field it came from. If you cannot, it is a claim, not data.
How to check yours
Search your codebase for ratingValue, reviewCount, aggregateRating. For each one, trace it back to source data. Anything hardcoded is a claim someone invented.
2. Duplicate Entities
Our layout emitted three sibling blocks — Organization, WebSite and ProfessionalService — none of which carried an @id.
That looks thorough. It is actually the opposite.
ProfessionalService is a subtype of LocalBusiness, which is a subtype of Organization. So two of those blocks described the same company, with no marker saying so. A crawler has to infer that the Organization and the ProfessionalService are one entity. Sometimes it does. Sometimes you get two thin entities instead of one strong one.
The pages made it worse: the About and Contact pages each restated a partial copy of the organisation inline, creating further look-alikes.
The fix: one node, referenced everywhere
Merge the overlapping types into a single node with a stable @id, and have everything else point at it:
Every provider, publisher and author becomes { "@id": "..." } instead of a repeated inline object. One entity, described once, referenced from everywhere.
A useful test: script a pass over your rendered HTML that collects every defined @id and every referenced @id, then diff them. Any reference without a matching definition is a dangling pointer telling crawlers about an entity that does not exist.
3. Claims About Things That Are Not There
Our WebSite node advertised a SearchAction:
The site has no search. That endpoint has never existed.
This is the most common category of schema rot, because it survives redesigns. Someone copies a schema template, it describes a feature the site does not have, and nothing ever fails.
Other frequent offenders worth grepping for:
speakableon a marketing site — it is a news-publisher feature and does not applyHowTomarkup where Google retired the rich result in 2023FAQPagewhere the questions exist only in the markup and never appear on screen- Opening hours, price ranges and service areas copied from a template and never checked
The FAQ one deserves emphasis: schema must describe visible content. If a question is in your JSON-LD but not on the page, that is cloaking, however unintentional.
4. Fake Freshness
A smaller one, but it compounds.
Five of our pages emitted dateModified: new Date() — evaluated at build time. Our sitemap did the same for lastModified. So a dependency bump with no copy change told crawlers that every page on the site had just been updated.
Crawlers notice. Repeatedly claiming freshness you do not have trains them to discount your dates entirely, which is strictly worse than reporting an older, accurate one.
Replace build timestamps with a content constant you bump deliberately when the copy actually changes.
5. Schema Built From Untrusted Input
One that only bites once, but bites hard.
JSON-LD is injected with dangerouslySetInnerHTML. If any value in that object comes from user input — a review, a comment, a product name from a third-party feed — and it is not serialised safely, you have handed an attacker a script tag on every page that renders it.
JSON.stringify escapes quotes but **does not escape </script>**. A value containing that sequence closes your JSON-LD block early and everything after it is parsed as HTML.
If your structured data is built entirely from your own constants, as ours is, this is not an issue today. It becomes one the moment a CMS or a feed is introduced, which is exactly when nobody re-reads the schema layer.
Validating Without Guessing
Two checks worth automating, because neither is obvious from reading the source:
Parse everything in the built output. Extract every <script type="application/ld+json"> from your rendered HTML and JSON.parse each one. A single trailing comma or unescaped character silently invalidates a whole block, and nothing in your build will tell you.
**Diff defined against referenced @ids.* Walk the parsed objects, collect every node that defines* an @id and every stub that only references one. Anything referenced but never defined is a pointer to an entity that does not exist.
Both run in a few lines of Node against your build output, and both catch things Google's Rich Results Test will not — it validates one URL at a time and says nothing about whether your entities join up across the site.
The Audit, In Order
If you do nothing else, do these four in this sequence:
- Grep for hardcoded numbers in your JSON-LD — ratings, review counts, employee counts. Trace each to a source or delete it.
- Count your entities. More than one node describing your company means you are splitting your own identity.
- Read your schema as claims. Every property asserts something. Does the site actually do it?
- Check your dates are real, not build artefacts.
None of this improves rankings on its own. What it does is stop you making assertions you cannot support — which, when the assertion is a five-star rating you invented, is worth considerably more than a rich result.