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.
Deep Link Landing Pages
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."
On Deep Link Landing Pages
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:
- Go to Search Console.
- Navigate to Settings > URL Parameters.
- 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://.
Deep Link Domain Canonicalization
When Your Deep Link Domain Shows Content
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" />
When Your Deep Link Domain Is Separate from Your Content Domain
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:
- Enter a URL you suspect might have duplicate issues.
- Check "Google-selected canonical" in the results.
- If the Google-selected canonical differs from your declared canonical, investigate.
Site Search
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.