The same piece of content on a WordPress site is often reachable through more than one URL without anyone intending it — http vs https, with vs without www, trailing slash vs none, and tracking parameters appended by ad campaigns all technically point to different URLs as far as a crawler is concerned, even though a human sees the same page.

Where the duplicate URLs actually come from

https://example.com/post-name/
https://example.com/post-name
https://www.example.com/post-name/
https://example.com/post-name/?utm_source=newsletter
https://example.com/post-name/?ref=homepage

Without a canonical tag, a search engine can end up indexing several of these as separate pages, splitting whatever ranking signal that content would otherwise have concentrated in one place.

Self-referencing canonical tags

add_action('wp_head', function() {
    if (is_singular()) {
        echo '<link rel="canonical" href="' . esc_url(get_permalink()) . '" />';
    }
}, 1);

This tells search engines that regardless of which URL variation was used to reach the page, this is the one to actually index. Most SEO plugins already do this — worth checking before adding a duplicate.

Server-level redirects for the structural variants

Canonical tags are a hint, not a hard rule — search engines mostly respect them, but not always. A 301 redirect is a stronger, unambiguous signal for the variants that should never be reachable at all:

# Nginx: force https and non-www
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}
server {
    listen 443 ssl;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

Tracking parameters specifically

Rather than trying to redirect away every possible UTM combination, the canonical tag is the right tool here — it lets the tracked URL work normally for the visitor (so your analytics still capture the campaign source) while telling search engines to consolidate ranking signal to the clean, parameter-free version. Trying to strip parameters via redirect instead would break attribution in your analytics, which is usually not worth the tradeoff.