Next.js SEO: Avoiding the Canonical Trap - By Sourav Mishra (@souravvmishra)

Why is your site getting de-indexed? I dive into the 'Canonical Trap' in Next.js 15+ and how to handle dynamic metadata correctly for better SEO.

BySourav Mishra3 min read

You did everything right: generateMetadata, sitemaps, OpenGraph images. Yet, Google Search Console shows "Duplicate, Google chose different canonical than user."

Welcome to the Canonical Trap.

In this guide, I, Sourav Mishra, Co-founder of Codestam Technologies, will explain why this happens in Next.js applications and how we solved this for our enterprise clients.

The Problem: Query Parameters & Trailing Slashes

Next.js, by default, treats /product and /product/ as the same page. Similarly, /blog?sort=new serves the same content as /blog.

If you don't explicitly tell Google which one is the "master" copy, it gets confused.

The Solution: Explicit Canonical Tags

In Next.js 14/15/16, you should always define the canonical base URL in your root layout metadata.

// app/layout.tsx
import type { Metadata } from 'next';

export const metadata: Metadata = {
  metadataBase: new URL('https://souravvmishra.site'),
  title: 'My App',
  alternates: {
    canonical: './', // Auto-resolves to the current path via relative URL
  },
};

Using ./ is a "magic" relative path that Next.js resolves to the full absolute URL of the current page, stripping query parameters automatically.

Dynamic Metadata with generateMetadata

For dynamic pages like blog posts, you usually want the canonical to match the slug.

// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }): Promise<Metadata> {
  const { slug } = params;
  
  return {
    title: `My Blog Post: ${slug}`,
    alternates: {
      canonical: `/blog/${slug}`, // Explicit absolute path preferred here
    },
  };
}

Handling Multilingual Sites (i18n)

This is where it gets tricky. If you have /en/about and /fr/about, you need hreflang tags and a self-referencing canonical for each.

// app/[lang]/layout.tsx
export async function generateMetadata({ params }) {
  return {
    alternates: {
      canonical: `./`,
      languages: {
        'en-US': '/en',
        'fr-FR': '/fr',
      },
    },
  };
}

At Codestam Technologies, we handle multilingual SEO for several high-traffic e-commerce sites. We found that forgetting the languages alternate tag resulted in a 30% drop in regional traffic. Google needs to know explicitly that /fr/about is the French version of /en/about.

Key Takeaways

  1. Always set metadataBase: Without it, OG images and absolute links might break.
  2. Use alternates.canonical: Even on static pages.
  3. Check Search Console: Using the "Inspection" tool, verify what Google thinks your canonical is.

If you are dealing with performance issues affecting your SEO, read my post on Debugging Production CSS to ensure your Core Web Vitals are green.

Also, consider how URL State Management can improve shareability and crawlability.


This guide was written by Sourav Mishra, Co-founder of Codestam Technologies and a Full Stack Engineer.

Share this post

Cover image for Next.js SEO: Avoiding the Canonical Trap

You might also like

See all