How to Set a Canonical URL in Next.js (App Router)
Set canonical URLs in the Next.js App Router with alternates.canonical and metadataBase, including dynamic routes, query parameters, and common mistakes to avoid.
Quick answer: how do I set a canonical URL in Next.js?
In the App Router, set the canonical URL with the `alternates.canonical` field of the Metadata object, then set `metadataBase` in the root layout so relative canonical paths resolve to your production host. Use a static `metadata` export for fixed pages and `generateMetadata` for dynamic routes.
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Pricing",
alternates: {
canonical: "/pricing",
},
};Why canonical URLs matter
A canonical URL tells search engines which URL is the preferred version of a page when the same or similar content is reachable from multiple URLs. Without it, duplicate URLs from query parameters, trailing slashes, or www and apex hosts can split ranking signals or let Google pick the wrong canonical.
- Consolidate duplicate or near-duplicate URLs to one preferred URL.
- Avoid query-parameter and trailing-slash duplicates competing in search.
- Keep the canonical aligned with the URL you actually want indexed.
Set metadataBase so relative canonicals resolve
When canonical and Open Graph URLs use relative paths, set `metadataBase` in the root layout to your canonical production host. Next.js then resolves relative `alternates.canonical` and `openGraph.url` values into absolute URLs.
// app/layout.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
metadataBase: new URL("https://example.com"),
alternates: {
canonical: "/",
},
};Canonical URLs for dynamic routes
For dynamic segments such as app/blog/[slug]/page.tsx, build the canonical inside `generateMetadata` from the route params so every page gets its own canonical URL instead of a shared one.
import type { Metadata } from "next";
type PageProps = {
params: Promise<{ slug: string }>;
};
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { slug } = await params;
return {
alternates: {
canonical: "/blog/" + slug,
},
};
}Canonical URLs for pagination and query parameters
Filters, sorting, and tracking parameters create many URLs for one logical page. Point the canonical at the clean URL so search engines consolidate them, unless a paginated page is genuinely a distinct indexable page.
- Drop tracking parameters such as utm_source from the canonical.
- For filtered lists, canonicalize to the unfiltered page when the content is essentially the same.
- Keep page 2 and beyond self-canonical only when each page should be indexed independently.
Common Next.js canonical URL mistakes
Most canonical issues come from drift between the canonical, the Open Graph URL, and the real indexable route, or from host and slash inconsistencies.
- Canonical drift: `alternates.canonical` and `openGraph.url` pointing at different URLs.
- Mixing www and apex, or http and https, in the canonical host.
- Trailing-slash mismatch between the canonical and the route Next.js actually serves.
- Reusing one canonical across every dynamic route instead of per-page values.
- Setting a canonical to a noindex or redirected URL.
Verify the canonical URL
After deploying, confirm the rendered canonical link tag matches the URL you want indexed, then validate it in Google Search Console.
- View source and check the canonical link tag in the document head.
- Use the URL Inspection tool to see the Google-selected canonical.
- Confirm the canonical returns HTTP 200 and is not blocked by robots.
Generate canonical metadata quickly
Use the Next Metadata Generator to produce a typed App Router starter with the canonical URL, Open Graph URL, and Twitter fields aligned, then paste it into the route and adjust per-page values.
Next.js canonical URL scenarios
| Scenario | Where | Canonical approach |
|---|---|---|
| Static page | `metadata` export | Set `alternates.canonical` to the page path such as `/pricing`. |
| Dynamic route | `generateMetadata` | Build the canonical from route params, for example `/blog/` + slug. |
| Paginated or filtered list | `generateMetadata` | Point the canonical at the clean page URL without tracking or filter query params. |
Use FrameworkKit to generate the starter code, then review the output before shipping it in production.
Generate with Next Metadata GeneratorNext Metadata resources
Next Metadata generator
Generate typed App Router metadata, canonical URLs, Open Graph fields, and Twitter cards.
Next Metadata examples
Start from static page, dynamic route, and social preview examples before adapting the output.
Next.js Metadata API examples
Compare static metadata, generateMetadata, metadataBase, canonical URLs, and Open Graph fields.
Open Graph metadata in Next.js
Create consistent Open Graph and Twitter card previews for App Router pages.
Next Metadata generator vs manual SEO
Choose when a generated starter is enough and when dynamic pages need hand-written metadata.
FAQ
How do I set a canonical URL in the Next.js App Router?
Set `alternates.canonical` in the Metadata object, using a static `metadata` export for fixed pages or `generateMetadata` for dynamic routes, and set `metadataBase` in the root layout so relative canonical paths resolve to absolute URLs.
Should the canonical URL be absolute or relative in Next.js?
You can use a relative path in `alternates.canonical` as long as `metadataBase` is set. Next.js resolves it into an absolute URL using that base.
How do I set canonical URLs for dynamic routes?
Use `generateMetadata`, read the route params, and build the canonical from them, for example `/blog/` + slug, so each dynamic page has its own canonical URL.
Why is Google choosing a different canonical than mine?
Google treats your canonical as a signal, not a directive. If your canonical points at a duplicate, a noindex page, a redirect, or drifts from the Open Graph URL and internal links, Google may pick a different URL. Keep all signals consistent.
Do canonical and Open Graph URLs need to match?
For normal content pages they should identify the same public page. Build both from the same canonical URL input so social previews and the indexable page stay aligned.
Related tools
Zod to JSON Schema Converter
Use a free browser-only online converter to turn Zod 4 schemas into JSON Schema for Draft 2020-12, Draft 7, AJV, or OpenAPI-compatible output.
JSON to Zod Converter
Convert JSON to Zod schemas with strict objects, optional field inference, and inferred TypeScript types.
tsconfig Builder
Build sane TypeScript compiler presets for Next.js, Vite, libraries, Node, and monorepos.