---
title: "Stop your code from creating duplicate URLs with canonicals, redirects, and clean routing"
description: "Find the duplicate URLs your code creates by accident, then fix them with canonical tags, redirects, cookie sessions, and noindex, with framework examples."
canonical: "https://scalewithsearch.com/articles/canonical-tags-developers"
date: "2026-03-20"
modified: "2026-09-25"
---
## Site navigation

- [Scale With Search](https://scalewithsearch.com/)
- Real estate
  - Real estate
    - [Real estate](https://scalewithsearch.com/for/real-estate)
- Work
  - Start here
    - [Send your brief](https://scalewithsearch.com/work#send-your-brief)
    - [Prepare your six-question brief](https://scalewithsearch.com/work#prepare-your-six-question-brief)
  - Build
    - [Site build, content library with SEO, signal desk](https://scalewithsearch.com/work)
- For your business
  - Trades and home services
    - [Auto body and collision shops](https://scalewithsearch.com/for/auto-body-and-collision-shops)
    - [Foundation and home repair contractors](https://scalewithsearch.com/for/foundation-and-home-repair)
    - [Garage door and fencing contractors](https://scalewithsearch.com/for/garage-door-and-fencing-contractors)
    - [HVAC contractors](https://scalewithsearch.com/for/hvac-contractors)
    - [Janitorial and commercial cleaning companies](https://scalewithsearch.com/for/janitorial-and-commercial-cleaning)
    - [Locksmiths](https://scalewithsearch.com/for/locksmiths)
    - [Moving companies](https://scalewithsearch.com/for/moving-companies)
    - [Pest control companies](https://scalewithsearch.com/for/pest-control-companies)
    - [Plumbing and electrical contractors](https://scalewithsearch.com/for/plumbing-and-electrical-contractors)
    - [Restoration and water or fire damage companies](https://scalewithsearch.com/for/restoration-and-water-fire-damage)
    - [Roofing companies](https://scalewithsearch.com/for/roofing-companies)
    - [Towing companies](https://scalewithsearch.com/for/towing-companies)
    - [Tree services and landscaping companies](https://scalewithsearch.com/for/tree-services-and-landscaping)
    - [Solar installers](https://scalewithsearch.com/for/solar-installation)
    - [General contractors](https://scalewithsearch.com/for/general-contractors-and-construction)
    - [Paving, concrete, and flooring contractors](https://scalewithsearch.com/for/paving)
  - Practices and professional services
    - [Bookkeeping and tax practices](https://scalewithsearch.com/for/bookkeeping-and-tax-practices)
    - [Dental practices](https://scalewithsearch.com/for/dental-practices)
    - [Family and criminal defense law firms](https://scalewithsearch.com/for/family-and-criminal-defense-law-firms)
    - [Med spas and aesthetics practices](https://scalewithsearch.com/for/med-spas-and-aesthetics)
    - [Personal injury law firms](https://scalewithsearch.com/for/personal-injury-law-firms)
    - [Veterinary clinics](https://scalewithsearch.com/for/veterinary-clinics)
    - [Gyms and fitness studios](https://scalewithsearch.com/for/fitness)
    - [Therapy and outpatient health practices](https://scalewithsearch.com/for/therapy-and-outpatient-health)
    - [Medical billing companies](https://scalewithsearch.com/for/medical-billing)
    - [Insurance agencies](https://scalewithsearch.com/for/insurance-agencies)
    - [Financial advisors](https://scalewithsearch.com/for/financial-advisors)
    - [Property management companies](https://scalewithsearch.com/for/property-management)
    - [Recruiting and staffing agencies](https://scalewithsearch.com/for/recruiting-and-staffing)
    - [Architects and interior designers](https://scalewithsearch.com/for/architects-and-interior-designers)
    - [Logistics and supply chain companies](https://scalewithsearch.com/for/logistics-and-supply-chain)
  - Agencies, MSPs, and manufacturing
    - [IT and managed service providers](https://scalewithsearch.com/for/it-and-managed-service-providers)
    - [Machine shops and precision manufacturers](https://scalewithsearch.com/for/machine-shops-and-precision-manufacturing)
    - [Marketing agencies and freelancers](https://scalewithsearch.com/for/marketing-agencies-and-freelancers)
    - [SEO agencies and consultants](https://scalewithsearch.com/for/seo-agencies-and-consultants)
    - [Small manufacturers and fabricators](https://scalewithsearch.com/for/small-manufacturers-and-fabricators)
  - Restaurants, shops, studios, and nonprofits
    - [Restaurants and hospitality businesses](https://scalewithsearch.com/for/restaurants-and-hospitality)
    - [Retail stores and ecommerce sellers](https://scalewithsearch.com/for/retail-and-ecommerce)
    - [Photographers, event planners, and travel agents](https://scalewithsearch.com/for/photographers)
    - [Churches and nonprofits](https://scalewithsearch.com/for/churches-and-nonprofits)
  - [All industries](https://scalewithsearch.com/for/)
- Learn
  - For your office
    - [Office job guides](https://scalewithsearch.com/guides/)
    - [Browser calculators](https://scalewithsearch.com/tools/)
  - Start here
    - [How it works](https://scalewithsearch.com/how-it-works)
    - [Free Starter Kit](https://scalewithsearch.com/kit/business-memory-starter-kit.zip)
    - [Synthetic specimen](https://scalewithsearch.com/specimen/working-session-specimen.zip)
  - Guides
    - [The Complete Guide to Business Memory for AI Agents](https://scalewithsearch.com/articles/business-memory-for-ai-agents-guide)
    - [The Complete Small-Business Guide to AI Agent Governance](https://scalewithsearch.com/articles/ai-agent-governance-guide-small-business)
    - [The Complete Guide to Leaving Vendor AI Memory](https://scalewithsearch.com/articles/leaving-vendor-ai-memory-guide)
  - Articles by cluster
    - [Business memory](https://scalewithsearch.com/articles/business-memory-for-ai-agents-guide)
    - [Agent governance](https://scalewithsearch.com/articles/ai-agent-governance-guide-small-business)
    - [Migration and ownership](https://scalewithsearch.com/articles/leaving-vendor-ai-memory-guide)
  - For machines
    - [llms.txt](https://scalewithsearch.com/llms.txt)
    - [llms-full.txt](https://scalewithsearch.com/llms-full.txt)
    - [Machine view](https://scalewithsearch.com/?view=machine)
- Company
  - Evidence
    - [Proof](https://scalewithsearch.com/proof)
  - Company
    - [About](https://scalewithsearch.com/about)

# Stop your code from creating duplicate URLs with canonicals, redirects, and clean routing.

The marketing team adds `?utm_source=newsletter` to every campaign link. The product team ships sort and color filters. Nobody changes the page content. Search Console now reports thousands of "Duplicate without user-selected canonical" URLs. The category page you care about ranks below one of its own filtered copies.

A canonical tag tells search engines which URL is the authoritative version when several URLs serve the same or very similar content. Developers create duplicates through normal features: tracking parameters, session IDs, faceted filters, pagination, mobile variants, exposed staging sites, and alternate views. Each duplicate splits ranking signals such as backlinks and crawl attention across URLs that compete with each other.

To Google, `?utm_source=email` makes a separate URL, even though the content is identical. Ten campaigns, 5 traffic sources, and 100 pages can produce 5,000 duplicate URLs. A canonical tag consolidates them to one version and keeps the feature working.

## Find the duplicates your code creates

| Pattern | Examples | Why it duplicates | Scale of the problem |
|---|---|---|---|
| Parameters | `?utm_source=newsletter`, `?sort=price&color=blue`, `?page=2` | Every combination is a distinct URL | 100 products, 5 sort orders, 10 colors, and 3 sources give 15,000 variants |
| Protocol and host | `http://` and `https://`; `www.` and bare domain; `m.example.com` | Each host and protocol is a separate URL | Four variants of 1,000 pages give 4,000 URLs |
| Slashes and case | `/products` and `/products/`; `/Products` on a case-sensitive server | URL rules treat them as different addresses | Mixed internal links split authority |
| Pagination and infinite scroll | `/blog/page/2`; scroll that loads content without a URL change | Pages share templates; deep content without URLs is invisible to crawlers | Long series waste crawl effort |
| Faceted filters | `?brand=nike&size=10&color=red` and the same filters in another order | Filter sets overlap or repeat | 5 attributes with 10 options each give 100,000 combinations |
| Mobile and AMP variants | `m.example.com`, `/amp/article` | Separate URLs for the same content | Doubles the indexed URL count |
| Session IDs | `?PHPSESSID=abc123`, `;jsessionid=def456` | Every session creates new URLs | An infinite crawl space, and a risk of exposed user data |
| Staging sites | `staging.example.com`, a host's default staging domain | Staging copies production | Staging can outrank production |
| Alternate views | `?print=1`, `/article/pdf` | Same content, new URL | People link to the print view instead of the article |

## Implement the canonical tag

### Use the basic syntax

Place one tag inside `<head>`, with an absolute URL:

```html
<link rel="canonical" href="https://example.com/products" />
```

### Add a self-referencing canonical to every indexable page

Each indexable page should name itself as canonical, even with no duplicates today. A future parameter then cannot create an unintended duplicate. If a page should not appear in search at all, use `noindex` instead of a canonical.

### Use cross-domain canonicals for syndication

A canonical can point to another domain. When a partner republishes your article, the partner page can name your original as canonical:

```html
<link rel="canonical" href="https://example.com/original-article" />
```

The partner must add that tag. You cannot force a third party to canonicalize to you. Google may also ignore a cross-domain canonical when the sites look unrelated.

### Send canonicals in HTTP headers for non-HTML files

PDFs, images, and some API or headless responses cannot carry an HTML tag. Send a `Link` header instead:

```text
Link: <https://example.com/documents/original.pdf>; rel="canonical"
```

Apache:

```apache
<FilesMatch "\.pdf$">
  Header set Link "<https://example.com/documents/original.pdf>; rel=\"canonical\""
</FilesMatch>
```

Nginx:

```nginx
location ~ \.pdf$ {
  add_header Link "<https://example.com/documents/original.pdf>; rel=\"canonical\"";
}
```

### Canonicalize paginated pages to themselves

Give each page in a series its own self-referencing canonical:

```html
<link rel="canonical" href="https://example.com/blog/page/2" />
```

Google's pagination guidance recommends this. Do not point page 2 and later at page 1. That tells Google the later pages duplicate page 1, and the items only linked from them can become hard to discover.

Google stated in 2019 that it no longer uses `rel="prev"` and `rel="next"` for indexing. Other search engines may still read them, and they do no harm, but they do not replace crawlable links between pages.

A view-all page is a third option. Canonicalize the series to one page that lists everything. It only works for small sets, because a page with hundreds of items performs badly.

### Handle faceted navigation deliberately

Point low-value filter URLs at the base category page:

```html
<link rel="canonical" href="https://example.com/shoes" />
```

Link equity then consolidates on the category URL. Filter combinations with real search demand, such as "Nike running shoes size 10," can deserve their own indexable page with a self-referencing canonical. For filter pages you never want indexed, `noindex, follow` keeps them out of the index while crawlers still follow product links. [E-commerce faceted navigation SEO](/articles/ecommerce-seo-faceted-navigation) covers the full decision.

### Control parameters without the old Search Console tool

Google retired the Search Console URL Parameters tool in April 2022. It now works out parameter handling on its own. Use canonicals, consistent internal links, and robots.txt rules to guide it instead.

## Prevent duplicates in the architecture

### Redirect variants that have no purpose

A 301 redirect is stronger than a canonical, because users and crawlers land on the canonical URL. Redirect when a variant has no function, such as protocol, host, or trailing slash. Canonicalize when a variant has a function, such as filters or tracking, but should not rank alone.

Force HTTPS in Apache:

```apache
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,L]
```

Send `www` to the bare domain in Nginx:

```nginx
server {
  server_name www.example.com;
  return 301 https://example.com$request_uri;
}
```

Remove trailing slashes in Apache:

```apache
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [R=301,L]
```

Redirect straight to the final URL. [Redirect chains and 301s for developers](/articles/redirect-chains-301-developers) explains why chains hurt.

### Normalize URLs in the application

Normalize before you render, so duplicates never exist. In Express:

```javascript
app.use((req, res, next) => {
  const query = req.url.slice(req.path.length);
  if (req.path.length > 1 && req.path.endsWith('/')) {
    return res.redirect(301, req.path.slice(0, -1) + query);
  }
  if (/[A-Z]/.test(req.path)) {
    return res.redirect(301, req.path.toLowerCase() + query);
  }
  next();
});
```

### Keep sessions in cookies

Never put a session ID in a URL. Store it in an HTTP-only cookie. In PHP:

```php
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.use_trans_sid', 0);
```

In Express with `express-session`:

```javascript
app.use(session({
  secret: process.env.SESSION_SECRET,
  cookie: { httpOnly: true, secure: true },
  resave: false,
  saveUninitialized: false
}));
```

### Block crawl paths in robots.txt

Block crawlers from URL patterns that should never be crawled:

```text
User-agent: *
Disallow: /*?sessionid=
Disallow: /*?PHPSESSID=
```

On the staging host, block everything:

```text
User-agent: *
Disallow: /
```

Know the limit. Robots.txt stops crawling, not indexing. A blocked URL can still appear in results if other sites link to it. Google cannot see a `noindex` tag on a page it may not crawl. Protect staging with authentication, and use `noindex` on pages that must stay out of the index. [Robots.txt and crawl budget for developers](/articles/robots-txt-crawl-budget-developers) covers the rules.

### Use noindex where no canonical parent exists

```html
<meta name="robots" content="noindex, follow" />
```

Use `noindex` for filter combinations with no logical parent, internal search results, and account pages. It removes the page from the index instead of merging signals.

## Debug canonicals that Google ignores

Google treats a canonical as a strong hint, not a directive. When Google chooses another URL, Search Console reports "Duplicate, Google chose different canonical than user." Check five causes.

**Conflicting signals.** The tag names URL A, but internal links, the sitemap, and backlinks favor URL B. Align every signal on one URL.

**An invalid target.** The canonical URL returns 404, redirects, or sits in a chain. The target must return `200` and must not redirect. If `/products/` redirects to `/products`, point the canonical at `/products`.

**Chains.** A parameter URL names `/products/`, which names `/products`. Point every variant directly at the final URL.

**Multiple tags.** Two plugins or templates each inject a canonical, and they disagree. Google then may ignore both. Audit the rendered `<head>` and keep one tag.

**A non-indexable target, or mixed directives.** The canonical points to a page that is `noindex`, blocked in robots.txt, or behind a login. Or the page carries both `noindex` and a canonical. Choose one directive, and point canonicals only at public, indexable URLs.

Fix duplicate hosts and status codes first; [HTTP status codes for SEO](/articles/http-status-codes-seo) lists what each one tells a crawler.

## Implement canonicals by framework

### WordPress

Yoast SEO adds self-referencing canonicals and handles paginated archives. Override it in `functions.php`:

```php
add_filter('wpseo_canonical', function ($canonical) {
  if (is_singular('product')) {
    return 'https://example.com/products/canonical-slug';
  }
  return $canonical;
});
```

### Next.js

In the App Router, set the canonical through the Metadata API:

```javascript
export const metadata = {
  alternates: { canonical: 'https://example.com/products' },
};
```

In the Pages Router, use `next/head`. Build the URL from the path so query parameters drop out:

```jsx
import Head from 'next/head';
import { useRouter } from 'next/router';

export default function ProductPage() {
  const { asPath } = useRouter();
  const path = asPath.split('?')[0];
  return (
    <Head>
      <link rel="canonical" href={`https://example.com${path}`} />
    </Head>
  );
}
```

### Django

Build the canonical from the path, which excludes the query string, in a context processor:

```python
def canonical(request):
    return {"canonical_url": request.build_absolute_uri(request.path)}
```

Then use it in the base template:

```django
<link rel="canonical" href="{{ canonical_url }}" />
```

For a view that needs a fixed URL, pass it in the context:

```python
def product_view(request, slug):
    return render(request, "product.html", {
        "canonical_url": f"https://example.com/products/{slug}",
    })
```

### Shopify

Shopify generates canonicals for products, collections, and blog pages, and most themes print them from the global `canonical_url` object:

```liquid
<link rel="canonical" href="{{ canonical_url }}" />
```

Shopify drops most parameters from `canonical_url`. Check custom parameters and any app that adds its own tag.

## Handle campaign parameters with the clean-URL canonical

Keep the self-referencing canonical on the clean URL. `example.com/products?utm_source=email` then declares `https://example.com/products` as canonical, and analytics still records the campaign. Point internal links at clean URLs only.

The same split applies to every variant in this guide. A canonical keeps every variant reachable and suggests the preferred one. Use it for functional variants such as filters and tracking. A 301 moves users and crawlers to the preferred URL. Use it when the variant has no function, such as HTTP or `www`. [Technical SEO for developers](/articles/technical-seo-for-developers) puts canonicals in the context of the wider crawl and index system.

----

```text
                  .|########||.                                       .|########||.                                       .|########||.
               |##||.      .||##|.                                 |##||.      .||##|.                                 |##||.      .||##|.
             |#|.              .|#|.                             |#|.              .|#|.                             |#|.              .|#|.
           |#|                    |#|                          |#|                    |#|                          |#|                    |#|
         .#|                        |#.                      .#|                        |#.                      .#|                        |#.
        .#.                          .#|                    .#.                          .#|                    .#.                          .#|
       |#.                            .#|                  |#.                            .#|                  |#.                            .#|
      |#             ......             #|                |#             ......             #|                |#             ......             #|
     .#           ||#########|           #|              .#           ||#########|           #|              .#           ||#########|           #|
    .#.         |######||######|.        .#.            .#.         |######||######|.        .#.            .#.         |######||######|.        .#.
    #.        .##|###|##|#|######|        .#            #.        .##|###|##|#|######|        .#            #.        .##|###|##|#|######|        .#
   ||        |##|#||||||||||||#||#|        ||          ||        |##|#||||||||||||#||#|        ||          ||        |##|#||||||||||||#||#|        ||
   #        |#||||||||||||||||||||#|        #.         #        |#||||||||||||||||||||#|        #.         #        |#||||||||||||||||||||#|        #.
  ||       |#||||||||||||||||||||||#|       ||        ||       |#||||||||||||||||||||||#|       ||        ||       |#||||||||||||||||||||||#|       ||
  #       .#||||||||||||||||||||||||#|       #        #       .#||||||||||||||||||||||||#|       #        #       .#||||||||||||||||||||||||#|       #
 ||   ....|||#||||||##|#|||#|#||##||||....|. ||      ||   ....|||#||||||##|#|||#|#||##||||....|. ||      ||   ....|||#||||||##|#|||#|#||##||||....|. ||
 #.  .  ....|#  ....#|||   ||| .#|||  ....#. .#      #.  .  ....|#  ....#|||   ||| .#|||  ....#. .#      #.  .  ....|#  ....#|||   ||| .#|||  ....#. .#
 #   .  ||||#| .#####||  . .#| .#|||  ||||#   #.     #   .  ||||#| .#####||  . .#| .#|||  ||||#   #.     #   .  ||||#| .#####||  . .#| .#|||  ||||#   #.
.|   |||||  #. |#|||||. ||  #. |#||. .|||||   ||    .|   |||||  #. |#|||||. ||  #. |#||. .|||||   ||    .|   |||||  #. |#|||||. ||  #. |#||. .|||||   ||
||   |....  #. ....|#.      |. ...|. ....||   ||    ||   |....  #. ....|#.      |. ...|. ....||   ||    ||   |....  #. ....|#.      |. ...|. ....||   ||
#.  .||||||##||||||##||####|||||||#||||||#|   .#    #.  .||||||##||||||##||####|||||||#||||||#|   .#    #.  .||||||##||||||##||####|||||||#||||||#|   .#
#    .||####|###########################|.     #    #    .||####|###########################|.     #    #    .||####|###########################|.     #
#      .#||||||.#.|| # |. ..# #| #|||||#|      #    #      .#||||||.#.|| # |. ..# #| #|||||#|      #    #      .#||||||.#.|| # |. ..# #| #|||||#|      #
#      .#|||||| ..  |# ## |#| ...#|#|||#|      #    #      .#|||||| ..  |# ## |#| ...#|#|||#|      #    #      .#|||||| ..  |# ## |#| ...#|#|||#|      #
#      .#|||#|# .# .#| #| ##|.#..#|||||#|      #    #      .#|||#|# .# .#| #| ##|.#..#|||||#|      #    #      .#|||#|# .# .#| #| ##|.#..#|||||#|      #
#      .#||||||############|######|||||#|      #    #      .#||||||############|######|||||#|      #    #      .#||||||############|######|||||#|      #
#   |...||#||||||#||||#|#||||||#|||||#||| |.|  #    #   |...||#||||||#||||#|#||||||#|||||#||| |.|  #    #   |...||#||||||#||||#|#||||||#|||||#||| |.|  #
#. .. ||||# .|||##|.  |#| .|| || .|||#. #|  # .#    #. .. ||||# .|||##|.  |#| .|| || .|||#. #|  # .#    #. .. ||||# .|||##|.  |#| .|| || .|||#. #|  # .#
|| |  ...||  ...##| |  #| .|. |. #####  .. .| ||    || |  ...||  ...##| |  #| .|. |. #####  .. .| ||    || |  ...||  ...##| |  #| .|. |. #####  .. .| ||
|| ||||| || ||||#|  .  |. |. |#. ||||| |#| || ||    || ||||| || ||||#|  .  |. |. |#. ||||| |#| || ||    || ||||| || ||||#|  .  |. |. |#. ||||| |#| || ||
.# |.....#|....|#.||||.|||##.|#|....||.#||.#. #.    .# |.....#|....|#.||||.|||##.|#|....||.#||.#. #.    .# |.....#|....|#.||||.|||##.|#|....||.#||.#. #.
 #.|||||||#############################| ||| .#      #.|||||||#############################| ||| .#      #.|||||||#############################| ||| .#
 ||       ##||#||#||||||#||#||#||#||##.      ||      ||       ##||#||#||||||#||#||#||#||##.      ||      ||       ##||#||#||||||#||#||#||#||##.      ||
  #       .#|||||||||#||#|||||||||||#|       #        #       .#|||||||||#||#|||||||||||#|       #        #       .#|||||||||#||#|||||||||||#|       #
  ||       |#||||||||||||||||||||||#|       ||        ||       |#||||||||||||||||||||||#|       ||        ||       |#||||||||||||||||||||||#|       ||
  .#        |#||||||||||||||||||||#|        #.        .#        |#||||||||||||||||||||#|        #.        .#        |#||||||||||||||||||||#|        #.
   ||        |#||||||||||||||||||#|        ||          ||        |#||||||||||||||||||#|        ||          ||        |#||||||||||||||||||#|        ||
    #.        .######|#|#########|        .#            #.        .######|#|#########|        .#            #.        .######|#|#########|        .#
    .#.         |#####||||#####|         .#.            .#.         |#####||||#####|         .#.            .#.         |#####||||#####|         .#.
     |#           ||########||           #.              |#           ||########||           #.              |#           ||########||           #.
      |#             ......             #|                |#             ......             #|                |#             ......             #|
       |#.                            .#|                  |#.                            .#|                  |#.                            .#|
        |#.                          .#.                    |#.                          .#.                    |#.                          .#.
         .#|                        |#.                      .#|                        |#.                      .#|                        |#.
           |#|                    |#|                          |#|                    |#|                          |#|                    |#|
            .|#|.              .|#|.                            .|#|.              .|#|.                            .|#|.              .|#|.
               |##||.      .||##|                                  |##||.      .||##|                                  |##||.      .||##|
                 .||########||.                                      .||########||.                                      .||########||.

Scale With Search  2026  [scalewithsearch.com](https://scalewithsearch.com)
```
