- Google Search Console reporting "Duplicate, Google chose different canonical than user" usually means the page declares a canonical URL that is not the URL that returns a 200 response.
- A canonical pointing at a URL that redirects is a contradiction: the page tells Google "index this other URL" while that other URL sends Google back to the page.
- On a site we audited, 424 pages declared a non-www canonical on a www host, and the fix was one regex plus splitting the display URL variable from the host variable in the generator.
The symptom: a canonical that points at a redirect
The first signal is rarely a ranking drop. It is a Search Console report. Under Page indexing, a block of URLs appears with the reason "Duplicate, Google chose different canonical than user". That message is literal: the page told Google which URL should be indexed, Google looked at that URL, decided it was not the right one, and picked something else.
On a programmatic site, this arrives in bulk. You do not see one page. You see a cohort of them, all sharing a template, all sharing the same generator. The count is the clue. If 400 pages fail with the same reason and 4,000 do not, the difference is in the template or in a variable that feeds it, not in the content of any single page.
There is a second, quieter signal. The pages may still rank, so nobody escalates it. The canonical is treated as a hint, not a directive, and Google can ignore a hint. That is exactly why this defect survives for months: the site keeps working well enough that no one investigates, while the canonical signal is wasted on every affected URL.
The third signal is a redirect chain that should not exist. If the declared canonical URL responds with a 3xx status, the declaration is self-defeating. A canonical should point at a final, indexable, 200 URL. Pointing it at a redirect means the canonical target and the page resolve to the same place, which gives Google no reason to trust either signal.
How to diagnose it
Start from what the page actually says, not from what the CMS shows in its admin panel. The rendered HTML is the only source of truth.
curl -s https://www.domain.com/some-article | grep -i 'rel="canonical"'
That prints the canonical as delivered. Compare it, character by character, with the URL you requested. Watch for scheme, host, trailing slash, case in the path, and query parameters. A single character difference is a different URL to a crawler.
Then check what the canonical URL returns. This is the step most audits skip.
curl -I https://domain.com/some-article
If the answer is 308, 301 or 302, you have found the defect. The canonical points at a redirect. In the case we audited, the non-www host returned 308 Permanent Redirect to the www host, so every affected page was declaring a canonical that immediately redirected back to itself.
Run the same pair of commands across a sample of templates, not just one URL. Home page, one blog article, one programmatic page, one category. You are looking for which templates agree with the host and which do not.
Then pull the sitemap and check it against the same rules. A URL in the sitemap should return 200, should be indexable, and should not be blocked. Two failures show up here often:
- A sitemap URL that carries
noindex,nofollow. The sitemap asks for indexing while the page refuses it. One of the two is wrong. - A sitemap URL that resolves through a redirect, or that belongs to a folder that should never have been public.
Finally, search the site for directories that were never meant to be served. Backup folders, staging copies, and export dumps are common. A simple approach is to read the sitemap for anomalies and to probe obvious names, then confirm with a status check. In the audit described here, a folder named as a dated backup was publicly reachable and served more than 400 duplicate articles.
For scale, do not run curl by hand on thousands of URLs. Export the URL list from the sitemap, fetch each page, extract the canonical, fetch the canonical, and record the status code. Any script that can make HTTP requests will do. The output you want is a table of: requested URL, declared canonical, canonical status code, match or mismatch.
What the data showed
The site we audited was a programmatic SEO property in financial services. Its sitemap listed 4,314 URLs.
The programmatic pages, the bulk of the site, were correct. All of them declared a www canonical matching the live host. The problem was concentrated elsewhere: the blog, 423 articles, and the home page declared a canonical on the non-www host.
The non-www host was not serving content. A header check on it returned 308 Permanent Redirect to www. So the sequence for every affected page was: the page declared a non-www canonical, that canonical redirected to the www version, and the www version was the page itself. The canonical declaration resolved in a loop back to its own source.
The root cause was in the article generator. It used a single constant, SITE_URL, set to the bare domain without www. That one constant fed two different jobs: the text drawn onto the Open Graph image, and the URLs written into the page. The display string and the canonical host had been conflated. Because the programmatic template used a separate code path, it escaped the bug, which is why 4,314 URLs were fine and 424 were not.
The same audit turned up two more defects in the same crawl:
- The sitemap listed a page marked
noindex,nofollow. That is a direct contradiction and it was removed. - A backup folder was publicly served, containing more than 400 duplicate articles. Their canonical tags pointed at the company's old domain, which is worse than no canonical at all: it was actively nominating a host that no longer represented the site.
Defect inventory from the audit
| Defect | Scope | What the page declared | What the server returned | Effect on crawling |
|---|---|---|---|---|
| Non-www canonical on www host | 423 blog articles | canonical to non-www URL | 308 redirect to www | Canonical signal points at a redirect, Google may choose its own canonical |
| Non-www canonical on www host | Home page | canonical to non-www URL | 308 redirect to www | Same contradiction on the most linked URL on the site |
| Correct canonical | 4,314 programmatic URLs | canonical to www URL | 200 | None, template used a separate code path |
| Sitemap and robots contradiction | 1 page | Listed in sitemap | noindex,nofollow | Sitemap requests indexing, page refuses it |
| Public backup folder | 400+ articles | canonical to old domain | 200 | Duplicate content nominating a host that is no longer the site |
The fix
The canonical bug was a variable problem, so the fix was a variable problem. The generator had one constant doing two jobs. Split it.
// before: one constant, two meanings
const SITE_URL = "domain.com";
// after: display string separate from canonical host
const SITE_DISPLAY = "domain.com"; // text drawn on the OG image
const SITE_HOST = "www.domain.com"; // used to build canonical URLs
Then rebuild every URL that goes into a rel="canonical" from SITE_HOST, and only the rendered image text from SITE_DISPLAY. The rule to hold onto: any string that becomes a URL must come from a variable whose value is the exact host that returns 200.
For the 424 already-published pages, a rewrite was enough. One regex, applied to the stored article bodies:
# replace the non-www canonical host with the www host
# applied across the 423 articles plus the home page
s|https://domain\.com/|https://www.domain.com/|g
Scope that regex carefully. It must only touch canonical URLs, not body links to external sites and not image sources. Run it against a copy first and diff the output.
The other two defects have simpler fixes. Remove the noindex,nofollow page from the sitemap, or remove the robots directive, depending on which one reflects the intent. Do not leave both. For the backup folder, take it off the public web server entirely. If it must exist, it belongs somewhere not reachable over HTTP, and its canonical tags are irrelevant once it is gone.
After the rewrite, the canonical on every affected page should be a URL that returns 200 with no redirect. That is the whole test.
If you would rather not run this by hand across thousands of URLs, this is the kind of work covered under technical SEO.
How to verify the fix
Verification is a status code check, not a feeling. Before the fix, capture a baseline: for a sample of affected URLs, record the declared canonical and the status code of that canonical. You expect to see 308 or 301 in the baseline for the affected cohort.
After the fix, run the same check and expect 200 everywhere.
# what does the page declare
curl -s https://www.domain.com/some-article | grep -i 'rel="canonical"'
# does that canonical resolve without a redirect
curl -I https://www.domain.com/some-article
Run it across the full 424, not a sample. The bug was systemic, so the verification has to be systemic too. A script that walks the sitemap and reports any canonical whose status is not 200 will catch regressions as well as the current state.
What to measure before and after:
- Count of pages whose declared canonical returns a 3xx. Before: the affected cohort. After: zero.
- Count of pages where the declared canonical differs from the requested URL by host. Before: 424. After: zero.
- Count of sitemap URLs that return
noindexor a non-200 status. Before: at least one. After: zero. - Count of publicly reachable backup or staging paths. Before: one folder, 400+ articles. After: zero.
In Search Console, the "Duplicate, Google chose different canonical than user" reason should stop appearing for new crawls. Recrawling takes time and you do not control the schedule, so treat the status code checks as your primary verification and the Search Console report as confirmation that arrives later. No one can promise a date for that.
The durable fix is the variable split. The regex repaired the existing pages, but the generator will keep producing bad canonicals on every new article until SITE_HOST is the only source of URL strings. Fix the template and the backlog in the same change, or the backlog comes back.
Frequently asked questions
Is a canonical tag a directive or a hint?
It is a hint. Google can and does ignore it, which is why the Search Console reason reads "Google chose different canonical than user". That does not make it harmless. A canonical pointing at a redirect wastes the signal and leaves Google to decide on its own, and its decision may not match the URL you want indexed.
Why did only some pages break if they share a generator?
Because they did not share a code path. On the site we audited, the programmatic template built its URLs one way and the blog plus home page built them another. The programmatic pages used the correct www host, all 4,314 of them, while 423 articles and the home page used a constant set to the bare domain. One shared constant, two consumers, one of them wrong.
Can a canonical point to a URL that redirects?
It can, in the sense that the HTML will be served and crawled, but it is a contradiction. You are telling a search engine to index a URL that then tells it to go somewhere else. The canonical should name the final URL that returns 200. If your canonical target returns 308, fix the canonical, not the redirect.
What about a backup folder that is still publicly reachable?
Remove it from the public web root. A backup folder serving duplicate articles with canonicals pointing at an old domain is worse than a folder with no canonical tags at all, because it actively nominates a host that is not your site. Canonical tags inside a directory that should not be public are not a fix, deletion is.
Frequently asked questions
It is a hint. Google can and does ignore it, which is why the Search Console reason reads "Google chose different canonical than user". That does not make it harmless. A canonical pointing at a redirect wastes the signal and leaves Google to decide on its own, and its decision may not match the URL you want indexed.
Because they did not share a code path. On the site we audited, the programmatic template built its URLs one way and the blog plus home page built them another. The programmatic pages used the correct www host, all 4,314 of them, while 423 articles and the home page used a constant set to the bare domain. One shared constant, two consumers, one of them wrong.
It can, in the sense that the HTML will be served and crawled, but it is a contradiction. You are telling a search engine to index a URL that then tells it to go somewhere else. The canonical should name the final URL that returns 200. If your canonical target returns 308, fix the canonical, not the redirect.
Remove it from the public web root. A backup folder serving duplicate articles with canonicals pointing at an old domain is worse than a folder with no canonical tags at all, because it actively nominates a host that is not your site. Canonical tags inside a directory that should not be public are not a fix, deletion is.