When og:url and your canonical disagree, shares fragment
Two tags on one page, both answering the question "what is the address of this thing", and nothing in the stack that makes them agree. When they differ, every share is filed under whichever one the scraper happened to believe.
The short answer
og:url decides which address a share is attributed to, and the canonical decides which address search consolidates on. When they disagree, engagement splits across two URLs while ranking signals go to a third. They come apart through parameters, protocol, www, trailing slashes and pagination, and all five are avoidable.
What og:url is actually deciding
og:url is not decoration and it is not a duplicate of your canonical tag for a different audience. It is the identifier a platform uses to decide whether two shares are shares of the same object. Somebody posts your page from a newsletter link with tracking parameters attached, somebody else posts it from the address bar, and whether those two posts accumulate against one record or two is decided by what og:url says on each fetch.
Your canonical tag answers the same question for search engines and answers it through a different mechanism, with a different consumer and a different tolerance for disagreement. The two are independent declarations that happen to be about the same fact, which is the structural reason they drift: nothing in any framework validates one against the other.
What matters, then, is not that both tags exist. It is that both tags contain the same string, character for character. A canonical of https://example.com/guides/sharpening and an og:url of https://example.com/guides/sharpening/ are two answers, and the trailing slash is enough.
<!-- these two strings should be identical, and usually are not -->
<link rel="canonical" href="https://example.com/guides/sharpening" />
<meta property="og:url" content="https://example.com/guides/sharpening" />
<!-- what a page reached through a campaign link often serves instead -->
<link rel="canonical" href="https://example.com/guides/sharpening" />
<meta property="og:url" content="https://example.com/guides/sharpening?utm_source=newsletter&utm_medium=email" />Five ways the pair comes apart
Every one of these is a normal implementation decision that happens to produce two different strings. None of them is a mistake anyone made on purpose.
- The canonical is computed and og:url is echoed. This is the big one. Most systems build the canonical from the route, which strips query strings by design. Many of them build
og:urlfrom the request URL, because that value was already at hand. A page fetched with a campaign parameter therefore serves a clean canonical and a parameterisedog:url, and the two disagree only on the URLs people actually share. - Trailing slashes are handled by two different layers. Your framework normalises the route, your CDN or host rewrites the path, and each tag is generated on the side of that boundary its template happens to live on. The slash question is where this originates and this pair is where you notice it.
- One of them still has the wrong host. Staging domains, preview deployment hostnames and
wwwagainst the bare domain all end up baked into whichever tag reads from an environment variable rather than from the request. - Localised routes resolve differently. A site serving the same content at a language prefix commonly canonicalises to a default locale while
og:urlreports the locale actually served, or the reverse. Both behaviours are defensible on their own and they cannot both be right at once. - og:url is simply absent. The most common state of all. Three Open Graph tags shipped in a layout and the fourth left out, so the platform falls back to the URL it fetched, which is exactly the parameterised address you were trying to consolidate away from.
Comparing two strings on every page at once
One page is a two-line check you can run from a terminal, and the command is in the last section of this guide. The reason this check belongs to the crawl is that a mismatch almost never affects one page: it is a property of a template, or of a routing layer, and the interesting output is which of your templates disagree with themselves and on how many URLs.
This one needs the full crawl
This compares two values on the same page, and og:url is not in the single-page payload, so the comparison runs across the crawl rather than from one fetch.
The instant search on this site audits a single page, so rather than show you a verdict it cannot support, this guide sends you to the place the check actually runs.
What actually splits, and what does not
It is worth being precise here, because this check is frequently oversold as a ranking problem and it is not one. Search engines do not document og:url as a signal in how they choose a canonical. Your rel=canonical, your internal links and your sitemap do that work, and a wrong og:url does not undo them.
What does split is everything that accumulates on the platform side. Reaction and comment counts attach to the object, and two objects means two counts, each looking smaller than the real total. The scraper's cache is keyed the same way, so a fix you make and re-fetch for one variant leaves the other serving the old preview. And your referral analytics inherit the fragmentation, which is how a page that was shared widely reports as several pages that were each shared a little.
There is one honest indirect effect on search. A parameterised URL that circulates widely is a URL that gets discovered, linked and crawled, which is exactly the situation your canonical tag then has to resolve. Getting og:url right means fewer variants entering circulation in the first place, which is less work for the canonical to clean up after rather than a replacement for it.
Does this affect AI search?
Barely, and the honest answer is more interesting than a yes. Retrieval does not read og:url. A model answering from your page has the content, and the address it cites comes from wherever it found the document, not from a meta tag inside it.
The one place it can matter is deduplication. Systems that assemble a corpus have to decide whether two fetches are the same document, and self-declared identity is one of the cheap signals available for that. A page that consistently reports one address across its canonical and its Open Graph is easier to collapse into a single record than one that reports two, and a single record is what carries whatever accumulated authority you have.
That is a mechanism worth understanding and a thin reason to prioritise anything. The reason to fix this is the human side: consolidated counts, a cache you can actually clear, and analytics that describe reality.
Why two answers to one question is rated Important
Not because of what any single mismatch costs. It stays below Critical because no page is removed from anything: both URLs work, both are indexable, and the search canonicalisation this looks like a threat to is decided elsewhere.
It earns Important because of what a mismatch usually means about the system that produced it. Two disagreeing URL declarations in one head is evidence that more than one thing in your stack believes it owns the address of a page, and that belief does not stay contained. The same inconsistency shows up in sitemap entries, in internal links, in redirects and in the canonical itself. This check is cheap to run and it points at a class of problem, which is a better reason to rate it than the size of its own consequence.
The second reason is that the damage is invisible and cumulative. A split share count is not an error anyone sees, it is a number that was always smaller than it should have been, and it is not recoverable once the shares are out. Findings you can fix in advance but never repair retroactively deserve to sit above the polish list.
Making the two strings agree, and proving it
The check is a string comparison, and the only trick is running it against a URL with a query string on it, because that is the case that exposes the common failure.
# fetch the way a person shares it: with a campaign parameter attached
URL='https://example.com/guides/sharpening?utm_source=newsletter'
HEAD=$(curl -s "$URL")
CANON=$(printf '%s' "$HEAD" \
| grep -o 'rel="canonical" href="[^"]*"' | sed 's/.*href="//;s/"$//')
OGURL=$(printf '%s' "$HEAD" \
| grep -o 'property="og:url" content="[^"]*"' | sed 's/.*content="//;s/"$//')
echo "canonical: ${CANON:-MISSING}"
echo "og:url : ${OGURL:-MISSING}"
if [ "$CANON" = "$OGURL" ]; then
echo "agree"
else
echo "DISAGREE: this page publishes two addresses for itself"
fiRun it once against the clean URL and once with a parameter. Passing the first and failing the second is the signature of a canonical built from the route and an og:url built from the request, which is the most common version of this and the one that only ever misfires on shared links.
Fix it by generating both from the same function, not by editing both templates.
The reason this recurs after being fixed is that it gets fixed twice, in two places, by copying a corrected string into each. Give the page one canonical-URL helper, absolute, normalised, parameter-free, and have the <link> and the <meta> both call it. Then the two tags cannot disagree, and the next person to change your URL structure changes it once.
After the change, force a re-fetch through each platform's own debugger. The cached object is keyed on the old value, so the correction is not visible to anyone until something asks the scraper to look again.
Questions this check raises
- Should og:url and the canonical be the same?
- Yes, on almost every page. Both are answering the same question, which address is the real one, and two different answers means share counts accrue to one URL while ranking signals accrue to another. A template that derives both from one value is the fix.
- What does og:url actually control?
- It is the address a platform attributes the share to, which decides where engagement accumulates and which URL is shown when the link is unfurled. A page shared with a UTM parameter and no og:url has its engagement counted against the parameterised address.
- Do I need og:url if I have a canonical?
- Yes. Social scrapers do not read the canonical tag, so the canonical alone leaves them to attribute the share to whatever URL was pasted. They are read by different consumers and both need to be present.