Skip to content
Tolinku
Tolinku
Sign In Start Free
Marketing · · 6 min read

Canonical URLs for App Content: Avoiding Duplicate Content

By Tolinku Staff
|
Tolinku smart banners dashboard screenshot for marketing blog posts

When your app content exists on the web, it often lives at multiple URLs. Your website has the content page, your deep link domain has a redirect URL, and your app store listing has a preview page. Search engines see each URL as a separate page. Without canonical tags, they split ranking signals across these duplicates, weakening your search performance.

This guide covers how to set canonical URLs correctly for app content and deep link domains. For deep link SEO benefits, see SEO benefits of deep linking. For app landing page optimization, see SEO for app landing pages.

Where Duplicate Content Happens

Multiple URL Patterns for the Same Content

Your product page might be accessible at several URLs:

https://www.yourapp.com/products/shoes-x          ← Website URL (canonical)
https://links.yourapp.com/products/shoes-x         ← Deep link domain URL
https://www.yourapp.com/products/shoes-x?ref=email ← Tracked link
https://m.yourapp.com/products/shoes-x             ← Mobile subdomain
https://yourapp.com/products/shoes-x               ← Non-www variant

Each URL shows the same content. Without canonicalization, Google picks one URL to index (and may pick the wrong one) or splits PageRank across all of them.

Many deep linking platforms create their own landing pages at the deep link domain:

https://links.yourapp.com/abc123 → shows a preview/interstitial
https://www.yourapp.com/products/shoes-x → the actual content page

If the deep link landing page is indexable, Google might index it instead of your real content page.

Campaign URLs with Tracking Parameters

Marketing campaigns add tracking parameters to URLs:

https://www.yourapp.com/products/shoes-x?utm_source=email&utm_medium=newsletter
https://www.yourapp.com/products/shoes-x?ref=twitter
https://www.yourapp.com/products/shoes-x?campaign=summer_sale

Google treats each parameter combination as a potentially different page.

Setting Canonical Tags

On Your Content Pages

Every content page should have a self-referencing canonical tag:

<head>
  <link rel="canonical" href="https://www.yourapp.com/products/shoes-x" />
</head>

This tells search engines: "This is the authoritative URL for this content. Any other URL showing the same content is a copy."

If your deep link domain serves landing pages (interstitials, previews), they should either:

Option A: Not be indexable (preferred).

<head>
  <meta name="robots" content="noindex, nofollow">
</head>

Or return a X-Robots-Tag: noindex HTTP header.

Option B: Canonical to the content page.

<head>
  <link rel="canonical" href="https://www.yourapp.com/products/shoes-x" />
</head>

Option A is better because it prevents the deep link URL from competing with your content URL entirely.

Handling Tracking Parameters

Tell Google to ignore tracking parameters using Search Console:

  1. Go to Search Console.
  2. Navigate to Settings > URL Parameters.
  3. Mark utm_source, utm_medium, utm_campaign, ref, and other tracking parameters as "No: does not affect page content."

Also, ensure your canonical tag strips parameters:

<!-- Page: /products/shoes-x?utm_source=email -->
<link rel="canonical" href="https://www.yourapp.com/products/shoes-x" />
<!-- Note: no parameters in the canonical URL -->

www vs. Non-www

Pick one and redirect the other:

# Redirect non-www to www
server {
    server_name yourapp.com;
    return 301 https://www.yourapp.com$request_uri;
}

Set the canonical to your preferred version:

<link rel="canonical" href="https://www.yourapp.com/products/shoes-x" />

HTTP vs. HTTPS

All URLs should use HTTPS. Redirect HTTP to HTTPS:

server {
    listen 80;
    server_name www.yourapp.com;
    return 301 https://www.yourapp.com$request_uri;
}

The canonical tag should always use https://.

If links.yourapp.com shows content (not just redirects), it creates a duplicate content problem. Solutions:

Solution 1: Pure redirects. Your deep link domain only redirects; it never serves HTML content.

links.yourapp.com/products/shoes-x → 302 redirect → www.yourapp.com/products/shoes-x

A 302 (temporary) redirect is appropriate because the deep link URL is not the canonical destination. Google does not index 302 targets as duplicates.

Solution 2: Noindex on the deep link domain. If the deep link domain must serve content (e.g., for smart banners or previews), add noindex:

<!-- On links.yourapp.com pages -->
<meta name="robots" content="noindex">

Solution 3: Canonical to content domain. Set the canonical on deep link pages to point to the content domain:

<!-- On links.yourapp.com/products/shoes-x -->
<link rel="canonical" href="https://www.yourapp.com/products/shoes-x" />

This is the most common setup:

Content: www.yourapp.com (indexed, canonical)
Deep links: links.yourapp.com (redirects only, not indexed)

No canonicalization issues because the deep link domain does not serve indexable content.

Pagination and Filtered Views

If your app has list pages with filters (categories, sort order, price range), each filter combination creates a different URL:

/products?category=running
/products?category=running&sort=price
/products?category=running&sort=price&page=2

Solution: Canonical to the Base URL

For filtered views, canonical to the unfiltered base:

<!-- /products?category=running&sort=price -->
<link rel="canonical" href="https://www.yourapp.com/products?category=running" />

For paginated views, each page should self-reference:

<!-- Page 2 -->
<link rel="canonical" href="https://www.yourapp.com/products?category=running&page=2" />
<link rel="prev" href="https://www.yourapp.com/products?category=running&page=1" />
<link rel="next" href="https://www.yourapp.com/products?category=running&page=3" />

Hreflang for Multi-Language Apps

If your app content exists in multiple languages, use hreflang tags to tell Google about the language variants:

<head>
  <link rel="canonical" href="https://www.yourapp.com/en/products/shoes-x" />
  <link rel="alternate" hreflang="en" href="https://www.yourapp.com/en/products/shoes-x" />
  <link rel="alternate" hreflang="es" href="https://www.yourapp.com/es/products/shoes-x" />
  <link rel="alternate" hreflang="fr" href="https://www.yourapp.com/fr/products/shoes-x" />
  <link rel="alternate" hreflang="x-default" href="https://www.yourapp.com/en/products/shoes-x" />
</head>

Each language version should be its own canonical URL. Do not canonical all languages to the English version.

Verification

Google Search Console

Use the URL Inspection tool to check canonicalization:

  1. Enter a URL you suspect might have duplicate issues.
  2. Check "Google-selected canonical" in the results.
  3. If the Google-selected canonical differs from your declared canonical, investigate.

Search Google for your content and check which URL appears:

site:yourapp.com "product name"
site:links.yourapp.com "product name"

If content from your deep link domain appears in search, it is being indexed when it should not be.

Crawl Reports

Check your crawl reports for signs of duplication:

  • Duplicate without canonical: Google found multiple URLs with the same content and no canonical tag.
  • Duplicate, Google chose different canonical than user: Your canonical tag points to one URL, but Google disagrees and chose a different one.

Tolinku and Canonical URLs

Tolinku deep link routes use redirects (not content pages) by default, so they do not create duplicate content issues. When a user clicks a Tolinku deep link, they are redirected to the app (if installed) or to your configured fallback URL (if not installed). No indexable content is served at the deep link URL.

For the broader SEO strategy, see SEO benefits of deep linking. For app landing page optimization, see SEO for app landing pages.

Get deep linking tips in your inbox

One email per week. No spam.

Ready to add deep linking to your app?

Set up Universal Links, App Links, deferred deep linking, and analytics in minutes. Free to start.