Why this exists

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 starting point

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.

What was actually happening

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:

  • Ten policy pages under /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.
  • Two of those same pages had it worse: the canonical tag and the JSON-LD 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.
  • One page was missing its entire Open Graph block - no og:title, no og:image, nothing - meaning a shared link to it would render with no preview at all.
  • A collection page's 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 decision log

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.

Decision 1: Pick one source of truth before touching anything

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.

Decision 2: Check every page, not just the one that prompted the question

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.

Decision 3: Treat a missing tag as its own category, not a variant of a wrong one

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.

Decision 4: Validate the JSON after every batch of edits, not at the end

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.

Before and after

Before

  • 10 policy pages canonical-linking to URLs that 404 in production
  • 2 pages with three internally disagreeing URL references
  • 1 page sharing with no title, description, or image
  • 1 collection page mislabeled as a single article
  • 1 page with a silently broken, unclosed JSON-LD block

After

  • Every canonical, og:url, and JSON-LD url field verified against the sitemap
  • Full Open Graph block present on every content page
  • Collection pages correctly typed and self-referencing
  • Every JSON-LD block on the site re-parsed and confirmed valid

What this actually took

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.

Frequently asked questions

How would I know if this is happening on my own site?

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.

Why does a wrong canonical tag actually matter?

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.

Is this only a risk after restructuring a site?

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.

Not sure whether your own pages agree with themselves?

A short conversation is usually enough to tell whether there's a quick answer or something deeper worth tracing.