Skip to content

SEOPart of: Canonical tags

How to add a canonical tag in WordPress and Shopify

Where WordPress core, Yoast SEO, Rank Math, All in One SEO and Shopify themes print the canonical tag, how to change it for one page or a whole template, and how to fix the duplicates each platform tends to create.

getReport teamUpdated 26 Sept 20269 min read

To add a canonical tag in WordPress, you usually don't have to: WordPress core already prints a self-referencing canonical on posts and pages, and an SEO plugin such as Yoast SEO, Rank Math or All in One SEO extends it to every page type and gives each post a Canonical URL field in its Advanced settings. Shopify prints the canonical from the theme's canonical_url variable on every page. This guide shows where each platform sets the canonical, how to change it for one page or a whole template, and how to fix the duplicates each platform tends to create. For what the tag does and how Google uses it, read canonical tags explained.

Quick answer

  • WordPress core prints <link rel="canonical"> on single posts, pages and attachments, but not on the home page blog listing, categories, tags or other archives.
  • Yoast SEO, Rank Math and All in One SEO replace core's tag, print one on every indexable page type, and add a per-post field: edit the post → SEO box → Advanced → Canonical URL.
  • Use one SEO plugin. Two plugins, or a plugin plus a theme that prints its own, give a page two canonicals.
  • Shopify prints {{ canonical_url }} in theme.liquid. Variant URLs and products opened inside a collection canonicalise to /products/handle. There is no per-page canonical field in the admin.
  • Change the domain in one place: the WordPress Address and Site Address (Settings → General), or Shopify's primary domain (Settings → Domains).
  • Check the result with the free canonical checker, then URL Inspection in Search Console.

How WordPress sets the canonical tag

WordPress core, without an SEO plugin

Since WordPress 2.9, core has printed a canonical tag through the rel_canonical() function, hooked to wp_head. It runs only on singular views (posts, pages, custom post types and attachments) and uses the permalink, adding the comment page number when comments are paginated. Archives, the blog index, search results and the home page when it shows latest posts get no canonical from core.

For a small blog that is often enough. If you want to change the URL core prints, use the get_canonical_url filter (available since WordPress 4.6) in a small must-use plugin rather than the theme, so a theme update does not remove it:

PHP
<?php
// wp-content/mu-plugins/canonical.php
// Point one post's canonical at another URL, e.g. the original of a syndicated copy.
add_filter('get_canonical_url', function ($url, $post) {
    if ($post->ID === 1234) {
        return 'https://example.com/original-article/';
    }
    return $url;
}, 10, 2);

Do not confuse this with WordPress's redirect_canonical(), which is a different feature: it redirects visitors from non-standard URLs (such as ?p=123 or a URL without the trailing slash) to the permalink.

Yoast SEO

Yoast removes core's tag and prints its own on posts, pages, archives, the home page and paginated pages, where each page in a series points to itself.

To set a custom canonical for one post or page:

  1. Edit the post or page.
  2. Open the Yoast SEO box (below the editor or in the sidebar) and choose Advanced.
  3. Paste the full URL into Canonical URL, including https://.
  4. Update the post.

To change the canonical for a whole template, for example to strip a parameter, use the wpseo_canonical filter. Returning false removes the tag for that page.

PHP
add_filter('wpseo_canonical', function ($canonical) {
    if (is_search()) {
        return false; // no canonical on internal search results
    }
    return $canonical;
});

Rank Math

Rank Math works the same way: it replaces core's tag, prints a canonical on every indexable page, and has a per-post field under Rank Math box → Advanced tab → Canonical URL. For code, the filter is rank_math/frontend/canonical:

PHP
add_filter('rank_math/frontend/canonical', function ($canonical) {
    return $canonical; // change or return false as needed
});

All in One SEO

All in One SEO has a Canonical URL field in the Advanced tab of its settings box on each post and page, and a filter, aioseo_canonical_url, for template-wide changes.

Only one plugin, and no theme canonical

Each SEO plugin assumes it is the only one printing head tags. Two active SEO plugins, or a plugin plus a theme or page builder that prints its own canonical, produce two <link rel="canonical"> tags. When they disagree, Google may ignore both and choose for itself. The fix is to keep one SEO plugin; running Yoast and Rank Math together explains how to migrate settings from one to the other.

To find a theme canonical, search the theme's header.php and template parts for rel="canonical" or rel_canonical. Remove it in a child theme.

The domain in every WordPress canonical

Every canonical WordPress and its plugins print is built from the Site Address (URL) in Settings → General. If that still says http:// or www. after a move, every canonical on the site points at a URL that redirects. Update both addresses to the final https:// form, then clear any page cache.

How Shopify sets the canonical tag

Where it comes from

Every Theme Store theme has this line in layout/theme.liquid:

Liquid
<link rel="canonical" href="{{ canonical_url }}">

Shopify builds canonical_url for every page from the store's primary domain. Two Shopify patterns are handled for you:

URL a visitor may land onCanonical Shopify prints
/collections/shoes/products/runner-x/products/runner-x
/products/runner-x?variant=40123/products/runner-x

That is correct, and you should not override it. These URLs appear in Search Console as alternate pages with a proper canonical tag, which is expected.

The primary domain

The canonical uses the domain you set as primary in Settings → Domains. Make sure the domain you want indexed, with or without www, is primary and that the other domains, including the .myshopify.com address, redirect to it.

Reducing collection-path duplicates

Many themes link from collection pages to /collections/…/products/… URLs, using product.url | within: collection in the product card snippet. The canonical cleans that up for indexing, but your internal links still point at non-canonical URLs. Replacing {{ product.url | within: collection }} with {{ product.url }} in the theme's product card makes internal links match the canonical. You lose the collection-aware breadcrumb on the product page in some themes, so check that first.

Setting a custom canonical on one Shopify page

The Shopify admin has no canonical field. To override it for selected pages, create a metafield and use it in the theme:

  1. In Settings → Custom data → Products (or Pages, Collections), add a metafield definition, for example custom.canonical_url, of type URL.
  2. Fill it on the product that needs a different canonical.
  3. In theme.liquid, replace the canonical line:
Liquid
{%- if product.metafields.custom.canonical_url != blank -%}
  <link rel="canonical" href="{{ product.metafields.custom.canonical_url.value }}">
{%- else -%}
  <link rel="canonical" href="{{ canonical_url }}">
{%- endif -%}

Make sure the theme prints only one canonical after the edit, and that apps have not injected another.

How to check the result

The canonical checker reads every canonical tag in the page, so a second one from a theme, plugin or app shows up, and checks that the canonical URL answers 200 without redirecting. It also requests the http and www variants and compares og:url with the canonical. It checks one page per run, so test one page per template: the home page, a post or product, a category or collection, and a paginated archive.

Then open URL Inspection in Search Console for a few of the same pages. "User-declared canonical" should show your URL, and after the next crawl "Google-selected canonical" should match it.

Common mistakes

  • Pasting a relative URL into the Canonical URL field. Use the full https:// URL.
  • Setting a custom canonical to "merge" two different pages. A canonical is for duplicates; for a page that has moved or been merged, use a 301 redirect. See canonical vs redirect vs noindex.
  • Canonical plus noindex on the same page, pointing elsewhere. Pick one.
  • Leaving a custom canonical after the other URL is deleted. The page then points at a 404 and may drop out of the index.
  • Editing the canonical in the parent theme. The next update removes it. Use a child theme, an mu-plugin or a theme duplicate in Shopify.
  • Overriding Shopify's product canonical to include the collection path. It turns one indexable product into many.

Questions people ask

Does WordPress add canonical tags automatically?

Yes, on single posts, pages and attachments. WordPress core's rel_canonical() prints a self-referencing canonical built from the permalink. It does not add one to the home page blog listing, categories, tags or other archives. An SEO plugin such as Yoast SEO or Rank Math replaces core's tag and adds canonicals to those page types as well.

How do I change the canonical URL in Yoast SEO?

Edit the post or page, open the Yoast SEO box, choose Advanced and paste the full URL into the Canonical URL field, then update the post. Leave the field empty to keep the default self-referencing canonical. For template-wide changes, such as removing the canonical from search results, use the wpseo_canonical filter in a small plugin.

Can I edit the canonical tag in Shopify?

Not from the admin; Shopify prints it from canonical_url in theme.liquid, and the default is correct for most stores. To override it for selected pages, add a URL metafield, fill it on those pages, and change the canonical line in the theme to use the metafield when it is set. Keep exactly one canonical tag after the edit.

Why does my Shopify product have two URLs?

Because many themes link to products through the collection, as /collections/name/products/handle, while the product also lives at /products/handle. Shopify's canonical on the collection version points to /products/handle, so only that one is indexed. To make internal links match, change product.url | within: collection to product.url in the theme's product card snippet.

Check your site before and after Check