The Schema Drift Problem
Three tags that are supposed to agree, quietly not agreeing - and what it took to find every place it had happened, not just the one that got noticed first.
A single page can describe its own URL in at least three places -
the canonical link tag, the Open Graph og:url tag,
and, if the page carries structured data, the
url field inside its own JSON-LD. Nothing forces
those three to agree with each other, or with where the page
actually lives. They're just three separate strings, hand-written
or copy-pasted at three separate times.
This is what happened when a routine structured-data cleanup on this site turned up exactly that kind of disagreement, spread across more pages than expected, and what it took to actually confirm the fix rather than just quiet the symptom that got noticed first.
The original task was narrower: standardize the JSON-LD
organization schema across every page - drop a stale address
block, retire an old founder name, fix a couple of pages
referencing an @id that didn't resolve to anything.
Mechanical work, page by page.
Partway through, a stray thought worth checking: if the structured data had drifted, had anything else? The Open Graph tags sitting right above those JSON-LD blocks hadn't been part of the original task at all - but they hadn't been checked in a while either.
Cross-referencing each page's canonical tag against its own
og:url, and both against the live sitemap, surfaced
three distinct failure patterns instead of one:
/trust/ had canonical and
og:url tags both missing the
/trust/ path segment entirely - pointing at a
root-level URL that had never existed on the live site.
url field pointed at a URL that had never
existed anywhere, while that same page's og:url
pointed at a third, still-wrong address - three tags, three
different wrong answers, on one page.
og:title, no og:image, nothing -
meaning a shared link to it would render with no preview at
all.
og:url pointed at one specific
article inside it instead of at itself, alongside an
og:type of article on a page that
wasn't one.
None of this was visible from reading any single page in isolation. Each page looked internally plausible - a canonical tag, an og:url, values that resembled real URLs. The drift only showed up by comparing each signal against the others, and against ground truth.
The fixes were one-line URL corrections. Trusting them required a specific process, because the tempting shortcut - fix whichever value looks wrong - doesn't work when more than one value is wrong on the same page.
With three tags per page potentially disagreeing, the sitemap was the tiebreaker - it's generated from what's actually deployed, not hand-typed per page, which made it the one signal least likely to have drifted the same way the manual tags had.
The first mismatch found was on a single page. The instinct is to fix that page and move on. Instead, the same canonical-versus-og:url-versus-sitemap comparison ran across every page in the repository, which is what turned one known mismatch into ten.
The page missing its Open Graph block entirely wasn't caught by a "does the URL match" check, because there was no URL to compare - it required a separate presence check for each individual tag, run across every page that carried structured data.
Structural edits to a JSON-LD block are easy to get almost right - a dropped comma or an unclosed brace still looks fine on a quick read. Every batch of edits got parsed as JSON immediately afterward, which is also what caught an unrelated, pre-existing corruption on one page where a script tag had been left truncated and unclosed from an earlier edit, silently breaking that page's structured data before this pass ever started.
Every individual fix was a URL string swap. None of them were hard. What made this worth writing down is that the bug wasn't in any one tag - it was in the assumption that three independently-maintained references to the same page would stay in sync on their own. They don't, especially after a page gets moved into a subdirectory and only some of its self-references get updated to match.
The actual takeaway: when a page describes its own location in more than one place, that's a place worth diffing against something authoritative on a schedule, not just when a mismatch happens to get noticed. Auditing and fixing this kind of drift is part of the Web & Digital work done for client sites with the same quiet inconsistency.
Pull every page's canonical href and its
og:url content, and diff each pair against the
corresponding entry in your sitemap. Any page where those three
don't all agree is a candidate - and it's worth checking every
page, not just ones that seem likely, since drift tends to
cluster around whatever got restructured or moved most recently.
A canonical tag pointing at a URL that doesn't exist is telling search engines the "real" version of a working page is a page that 404s. Depending on how that gets interpreted, it can mean the working page never gets indexed under its actual address, or gets indexed inconsistently.
That's the most common trigger - moving a page into a subdirectory and updating the file path without updating every tag that hardcodes the old URL - but the same drift can happen any time a page's metadata gets copy-pasted from a template and only partially customized.
A short conversation is usually enough to tell whether there's a quick answer or something deeper worth tracing.