Docs
Set the canonical address
Set the canonical address
The same page is usually reachable at several addresses: with and without www, with a trailing slash or without, with ?utm_source=newsletter or ?sort=price at the end. The canonical tag tells Google which one is the real address, so the page is listed once, with all its links counted together, instead of competing with copies of itself.
Two things go wrong. Without the tag, Google guesses, and it sometimes picks the address with tracking parameters. Worse is a canonical that points to another site, almost always the staging address (https://staging.example.com/…) left over after a launch: it tells Google that the real page lives somewhere else, and the live page can drop out of results in favour of an address nobody should see. The site checklist marks a missing tag as Fix and a canonical on another host as Check.
What a correct canonical looks like
One tag in the page's <head>, with the full address of the page itself:
<link rel="canonical" href="https://example.com/services/">
| Canonical | Result |
|---|---|
| the page's own address on your domain | OK |
your domain with or without www |
OK (but use the version the site redirects to, see one address, with or without www) |
another host, e.g. staging.example.com or example.webflow.io |
Check |
| no tag | Fix |
Every page points to itself, not to the home page. A canonical to the home page on every page tells Google that all pages are copies of the home page.
Check it yourself
Replace example.com with your site, and also try an inner page:
curl -sL https://example.com | grep -ioE '<link[^>]+rel="?canonical"?[^>]*>'
curl -sL https://example.com/services/ | grep -ioE '<link[^>]+rel="?canonical"?[^>]*>'
Each command should print one tag whose href is that page's own address on the live domain.
How to fix
WordPress
WordPress itself adds a canonical only to single posts and pages. Yoast SEO and Rank Math add it to every page (home, categories, archives) and are the simplest fix for a missing tag: install one, and the tag appears.
When the canonical points to the staging address, the old address is still stored somewhere:
- Settings → General: WordPress Address (URL) and Site Address (URL) must both be the live address.
- A single page can have its own canonical: Yoast SEO box → Advanced → Canonical URL, or Rank Math → Advanced → Canonical URL. Empty means "this page", which is what you want.
- If the staging address is still in the database (posts, widgets, plugin settings), replace it everywhere. With WP-CLI, try a dry run first:
wp search-replace 'https://staging.example.com' 'https://example.com' --all-tables --dry-run
wp search-replace 'https://staging.example.com' 'https://example.com' --all-tables
Back up the database before the second command. Without WP-CLI, a search-and-replace plugin does the same. Then clear the page cache.
Node.js and Next.js
In the Next.js App Router, set metadataBase once in the root layout and the canonical in each page:
// app/layout.js
export const metadata = {
metadataBase: new URL('https://example.com'),
};
// app/services/page.js
export const metadata = {
alternates: { canonical: '/services/' },
};
For dynamic pages, return it from generateMetadata:
export async function generateMetadata({ params }) {
const { slug } = await params;
return { alternates: { canonical: `/blog/${slug}` } };
}
Do not put alternates.canonical in the root layout: every page that does not override it inherits it, and the whole site points to the home page. If the canonical shows a preview address, metadataBase is built from an environment variable that is not set for production.
Laravel
In the layout's <head>:
<link rel="canonical" href="{{ url()->current() }}">
url()->current() is the current address without the query string, so ?utm_… and ?page= variants all point to the clean address. Behind a load balancer or Cloudflare, make sure Laravel trusts the proxy, otherwise it builds http:// addresses. If you build the canonical from config('app.url'), check APP_URL in the live .env and run php artisan config:cache.
Static HTML
Add the tag to the <head> of every page, each with its own address:
<link rel="canonical" href="https://example.com/contact/">
Use the exact form the site really uses: https://, with or without www, and with or without the trailing slash, as the site redirects.
Shopify
Shopify themes print the canonical in theme.liquid. If the checklist finds none, open Online Store → Themes → … → Edit code → Layout → theme.liquid and add this inside <head>:
<link rel="canonical" href="{{ canonical_url }}">
If it points to *.myshopify.com or an old domain, set the right domain as primary under Settings → Domains.
Wix
Wix adds the canonical on its own, based on the connected domain. If it points to the wrong host, check that your domain is connected and set as the primary domain. A single page can have a custom canonical in the page's SEO settings (Advanced SEO); leave it empty unless you have a reason.
Webflow
Open Site settings → SEO and set Global canonical tag URL to your live domain, e.g. https://example.com (without a path; Webflow adds each page's path). Also check that the domain is set as the default under Site settings → Publishing, then Publish.
Squarespace
Squarespace adds the canonical on its own and it cannot be edited. It uses the primary domain: if it shows *.squarespace.com or an old domain, set the right one as primary in the Domains settings.
After a launch or a move
Check the canonical on the day a site goes live and after every domain change. It is one of the few mistakes that does not show on the page, does not break anything a visitor sees, and quietly sends Google to the wrong address for weeks.
Confirm the fix
Run the curl commands from Check it yourself again, then open the target in PulseKeeper and click Check now on the Checklist tab. The item turns OK within a few minutes instead of at the next daily run. In Google Search Console, URL inspection shows both the canonical you declared and the one Google chose.