Canonical tag conflict checker
Enter the page address and the address in its canonical tag and see at once whether they match: in the example, a product page ending in #details matches, because fragments are dropped before comparing. A canonical on another domain is flagged as high risk. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Page url
- https://www.example.com/products/blue-widget#details
- Canonical url
- https://www.example.com/products/blue-widget
- Normalized page url
- https://www.example.com/products/blue-widget
- Normalized canonical url
- https://www.example.com/products/blue-widget
- Risk
- none
A canonical tag can look perfectly valid while quietly pointing search engines away from the page you intended to index. This checker compares a page URL with the URL declared in its canonical tag, reports whether they match after safe URL normalization, and calls out a different destination domain as high risk. It is a focused way to review template changes, migrations, publishing mistakes, and suspicious canonical values before they weaken indexing signals or consolidate authority into the wrong address.
What a canonical conflict means
A canonical tag tells search engines which URL should represent a page when several addresses contain the same or substantially similar material. A self-referencing canonical usually repeats the page URL and gives crawlers an unambiguous preferred address. A conflict appears when the submitted page URL and canonical value resolve to different normalized URL strings.
That difference may be intentional, such as a tracking URL consolidating into a clean product URL, but it deserves review because the declaration can transfer indexing signals away from the current address. The checker returns both original parsed URLs, comparison forms without fragments, a match flag, a conflict flag, and a plain-language message. It does not crawl either destination or guess whether two pages contain equivalent content.
Instead, it answers the narrow technical question reliably: does this declared canonical identify the submitted page URL? That makes the result suitable for deployment checks, spreadsheet audits, content quality workflows, and automated tests where a deterministic yes-or-no comparison is more useful than an opaque SEO score.
How URLs are validated and compared
Both fields are parsed as URLs, and malformed values, relative paths, unsupported schemes, missing values, or addresses containing embedded credentials are rejected as invalid input. During comparison, standard URL parsing normalizes details such as host casing and explicit default ports. Fragments are removed because they identify a location within a document rather than a separate canonical document.
Other meaningful components remain, including the scheme, hostname, pathname, query string, and nondefault port. The returned normalized values show exactly what was compared, allowing reviewers to understand why a mismatch was reported and decide whether the difference reflects policy or a defect.
Why cross-domain canonicals receive high risk
A canonical that points to another hostname can be legitimate, particularly for syndicated articles, licensed content, or a carefully controlled domain migration. It can also be a damaging production mistake caused by a staging-domain setting, an old brand hostname, a copied template, or compromised markup. For that reason, this checker labels any hostname change as cross-domain and assigns high risk, even when the external target was chosen deliberately.
High risk is a review priority, not a claim that the tag is certainly wrong. Confirm that both site owners understand the consolidation, that the target is the intended authoritative version, and that related redirect, sitemap, internal-link, and hreflang signals support the same decision. Same-domain mismatches receive a warning because they may still suppress the submitted URL or consolidate it into an unintended category, product, locale, or parameter variant.
A matching canonical receives no risk. Run this check when publishing pages, changing routing rules, moving between domains, altering CMS templates, or importing content. The deterministic output also works well in continuous integration: fail builds on unexpected conflicts and require explicit review for every cross-domain result.
What you can do with it
Catch staging-domain leakage
Check a production page against its emitted canonical and immediately flag a forgotten staging or preview hostname as high risk.
Test canonical templates after deployment
Compare representative page and canonical URL pairs after a CMS or routing release so unintended path, query, scheme, or host changes are visible.
Review migration signals
Identify cross-domain declarations during a site move and send each high-risk result for confirmation against the approved migration map.
FAQ
What counts as a match?
Meaningful differences such as paths, queries, schemes, and hostnames remain significant.
Why is a cross-domain canonical marked high risk?
It can deliberately consolidate syndicated or migrated content, but it can also send indexing signals to a staging site, obsolete domain, or unrelated owner. Every such result deserves review.
Does the checker fetch either URL?
No. It performs deterministic validation and comparison only.
Does a mismatch always mean the canonical is wrong?
No. Parameter variants, duplicate pages, syndication, and migrations can use a different canonical intentionally. A mismatch is a review signal; a cross-domain mismatch is prioritized as high risk.
Are www, https and trailing-slash variants treated as the same URL?
No. Tracking tags, www hosts, http versus https and trailing slashes are compared exactly as written, because rewriting them could hide a real template inconsistency. Only capital letters in the host, explicit default ports and fragments are normalized.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/seo/canonical-conflict-check \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"page_url":"https://www.example.com/products/blue-widget","canonical_url":"https://www.example.com/products/blue-widget"}'const res = await fetch("https://api.kit.forhosting.com/seo/canonical-conflict-check", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"page_url": "https://www.example.com/products/blue-widget",
"canonical_url": "https://www.example.com/products/blue-widget"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/seo/canonical-conflict-check",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"page_url": "https://www.example.com/products/blue-widget",
"canonical_url": "https://www.example.com/products/blue-widget"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/seo/canonical-conflict-check", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"page_url":"https://www.example.com/products/blue-widget","canonical_url":"https://www.example.com/products/blue-widget"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"page_url":"https://www.example.com/products/blue-widget","canonical_url":"https://www.example.com/products/blue-widget"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/seo/canonical-conflict-check", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"page_url": "https://www.example.com/products/blue-widget",
"canonical_url": "https://www.example.com/products/blue-widget"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "seo.canonical_conflict_check",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
How URLs are validated and compared
Enter the absolute HTTP or HTTPS address of the page and the absolute value found in its canonical tag. The checker does not remove tracking parameters, rewrite www hosts, force HTTPS, decode application-specific slugs, or assume that trailing-slash variants are equivalent. Those transformations could hide a real template inconsistency.
Technical questions
Are relative canonical values accepted?
No. The canonical value must be an absolute HTTP or HTTPS URL so the destination is explicit and can be compared without inventing a base address.
How much does the API check cost?
Each API request costs $0.002. The browser version can run the same deterministic comparison locally for free.