---
title: "Add canonical URLs with the Next.js Metadata API"
description: "Next.js App Router resolves metadata per route, so each page can declare its own canonical URL instead of inheriting one global value. Set alternates.canonical in the page metadata export or generateMetadata, keep it absolute and self-referencing, and make sure template redirects and trailing-slash handling agree with the URL you declare."
url: "https://seo-mcp.io/t/add-canonical-urls-with-the-next-js-metadata-api"
---

# Add canonical URLs with the Next.js Metadata API

Next.js App Router resolves metadata per route, so each page can declare its own canonical URL instead of inheriting one global value. Set alternates.canonical in the page metadata export or generateMetadata, keep it absolute and self-referencing, and make sure template redirects and trailing-slash handling agree with the URL you declare.

## Prerequisites

- Next.js App Router project deployed behind a stable canonical host
- A single site URL constant available to metadata exports

## Steps

1. **Set the canonical URL policy** — Choose one absolute form per page: HTTPS, lowercase host and a fixed trailing-slash rule. Store the production origin in a constant such as siteUrl so metadata, links and redirects all read the same value.
   Check: The siteUrl constant resolves to the production origin, and the http, www and apex variants all redirect to it.
2. **Keep app/layout.tsx free of a canonical** — Export a metadata object from app/layout.tsx for shared fields only. Do not put a route-specific alternates.canonical there, because every route that forgets to override it inherits the homepage URL.
   Check: grep -n 'alternates' app/layout.tsx shows no route-specific canonical value.
3. **Add alternates.canonical per static page** — In each page.tsx export metadata with alternates: { canonical: siteUrl + '/pricing' }. Keep the path lowercase and identical to the deployed route, then rebuild so the tag is rendered in the initial HTML.
   Check: curl -s your production domain/pricing | grep -c 'rel="canonical"' returns 1 and the href equals the pricing URL.
4. **Use generateMetadata for dynamic routes** — In app/blog/[slug]/page.tsx await params, load the post and return { alternates: { canonical: siteUrl + '/blog/' + slug } } from generateMetadata so each record gets its own URL instead of the index canonical.
   Check: curl -s your production domain/blog/<slug> | grep -o 'rel="canonical"[^>]*' prints the exact post URL.
5. **Remove duplicate sources and confirm** — Delete hardcoded rel=canonical tags in components, CMS widgets and SEO plugins. After deploy, fetch one page per route type, then request validation in the Search Console URL Inspection tool.
   Check: curl -s your production domain/<path> | grep -c 'rel="canonical"' returns 1 for each sampled route.

## Source

@seo-mcp on 2026-10-19: https://nextjs.org/docs/app/api-reference/functions/generate-metadata

Canonical: https://seo-mcp.io/t/add-canonical-urls-with-the-next-js-metadata-api
