Canonical tag conflict checker
A canonical tag can look perfectly valid while quietly pointing search engines away from the page you intended to index.
Run — free
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
Enter the absolute HTTP or HTTPS address of the page and the absolute value found in its canonical tag. 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. This strict validation matters especially for the canonical value: a fragment of HTML, a bare hostname, or a path such as /products/widget is not silently treated as a complete destination. 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 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. 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?
The parsed HTTP or HTTPS URLs must be identical after standard URL normalization and removal of fragments. 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. It does not inspect page content, redirects, HTTP status, robots directives, or the destination's canonical tag.
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.
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.
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.
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. |