Back to blog

Published Updated

SEO-Friendly URLs and Slugs in Next.js

By Tushar ChoudharyNext.js • "URL Structure • "Slugs • "Canonical • "Redirects • "Technical SEO

Create durable SEO-friendly URLs in Next.js with readable slugs, stable route rules, canonical metadata, redirects, parameter control and build validation.

SEO-Friendly URLs and Slugs in Next.js

An SEO-friendly URL is readable, stable and connected to one clear piece of content. It should help users understand where a link leads and help the application avoid duplicate route variants.

In Next.js, URL quality depends on more than a slug function. Route folders, content identifiers, canonical metadata, redirects, sitemap generation, internal links and static parameters must agree on the same public URL.

Quick Answer

Use:

  • lowercase readable words;
  • hyphens between words;
  • a short stable hierarchy;
  • one canonical host and path;
  • permanent redirects from retired paths;
  • internal links that point directly to the final route;
  • explicit rules for parameters, filters and pagination;
  • a stored slug rather than regenerating public URLs from every title change.

Google’s current URL structure guidance recommends simple, descriptive URLs, hyphens instead of underscores and as few unnecessary parameters as practical.

A URL Is a Public Identifier

Treat a published URL as an identifier, not a decorative label. Changing it can affect:

  • bookmarks;
  • backlinks;
  • analytics history;
  • canonical selection;
  • sitemap continuity;
  • social shares;
  • email and PDF links;
  • application integrations.

Use the title for editorial changes and the slug for stable identification. Change a slug only when the migration benefit is worth the redirect and monitoring cost.

Next.js URL decision map

Slug Rules

RulePreferredAvoid
Case/blog/inventory-guide/Blog/Inventory-Guide
Separator/website-cost-india/website_cost_india
Meaning/services/web-applications/page?id=42
LengthEssential topic wordsFull sentence and repeated categories
DatesOnly when useful to identityAdding a year that forces annual URL changes
CharactersLetters, numbers and hyphensSpaces, punctuation and unencoded reserved characters

Do not remove words mechanically if the result becomes ambiguous. A concise phrase should still distinguish the page.

Design the Route Hierarchy

A small service website might use:

/
/services
/services/web-applications
/blog
/blog/seo-friendly-urls-slugs-nextjs
/contact

The hierarchy should reflect how users browse the site, but it does not need to encode every category.

Avoid routes such as:

/india/delhi-ncr/delhi/web-development/services/company/best

Deep keyword paths are hard to maintain and can signal an architecture built for queries rather than users.

Store Slugs in Content

For a file-based blog, keep the slug in frontmatter:

---
title: "SEO-Friendly URLs and Slugs in Next.js"
slug: "how-to-create-seo-friendly-urls-slugs-nextjs"
---

The parser should validate:

  • slug is present;
  • slug is unique;
  • filename and slug follow the project convention;
  • public path is not duplicated by another record;
  • unsafe or unexpected characters are rejected.

Do not silently publish two posts with the same slug.

A Controlled Slug Function

Use a slug function for drafts or administrative input, then let an editor approve the public identifier.

export function toSlug(value) {
  return value
    .normalize("NFKD")
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9\\s-]/g, "")
    .replace(/[\\s_-]+/g, "-")
    .replace(/^-+|-+$/g, "");
}

This example is deliberately conservative and English-oriented. Products serving other languages need transliteration or Unicode rules appropriate to their users. Test empty results, duplicate slugs and very long inputs.

Next.js App Router Example

For a blog route at app/blog/[slug]/page.jsx:

export async function generateStaticParams() {
  const posts = await getAllPosts();
  return posts.map((post) => ({ slug: post.slug }));
}

export async function generateMetadata({ params }) {
  const { slug } = await params;
  const post = await getPostBySlug(slug);
  const url = `https://www.example.com/blog/${post.slug}`;

  return {
    title: post.title,
    description: post.metaDescription,
    alternates: { canonical: url },
    openGraph: { url },
  };
}

The page, metadata and structured data should read the same stored slug. Do not derive a second URL from the title inside generateMetadata.

Handle Missing Slugs

An unknown slug should return a real not-found response:

import { notFound } from "next/navigation";

export default async function BlogPost({ params }) {
  const { slug } = await params;
  const post = await getPostBySlug(slug);

  if (!post) notFound();

  return <Article post={post} />;
}

Avoid serving a generic successful page for every unknown route. Soft 404 behaviour can waste crawl signals and confuse users.

Canonical URL Logic

Canonical metadata identifies the preferred URL among duplicate or similar variants. It does not repair a broken route architecture by itself.

For each indexable page:

  • canonical uses the final HTTPS host;
  • canonical path matches the public slug;
  • Open Graph URL matches;
  • structured-data URL matches;
  • sitemap contains that URL;
  • internal links use that URL;
  • page returns 200;
  • page is not noindex.

See the Next.js canonical repair guide for full validation.

Redirect Slug Changes

When a published slug must change:

  1. define the new canonical route;
  2. add a permanent redirect from old to new;
  3. update internal links;
  4. update sitemap;
  5. update structured data and Open Graph;
  6. preserve the redirect long term;
  7. test for loops and chains;
  8. monitor old and new URLs.

Example:

// next.config.js
export default {
  async redirects() {
    return [
      {
        source: "/blog/old-inventory-guide",
        destination: "/blog/inventory-management-guide",
        permanent: true,
      },
    ];
  },
};

For static export or platform hosting, implement the redirect at the hosting layer supported by the deployment.

Host and Trailing-Slash Consistency

Choose:

  • final host, such as https://www.example.com;
  • trailing-slash policy;
  • lowercase-path policy.

Then make redirects, metadata and generated links agree. A redirect from HTTP apex to HTTPS apex and then to HTTPS www creates an avoidable chain. Configure the host layer to reach the final URL directly where possible.

Parameters and Filters

Parameters are legitimate when they change application state, but unmanaged combinations can create many crawlable URLs.

Examples:

/products?category=shoes&sort=price
/search?q=inventory
/blog?page=2

For each parameter, decide:

  • whether it changes primary content;
  • whether the URL should be indexable;
  • canonical target;
  • internal-link behaviour;
  • sitemap inclusion;
  • server response for invalid values.

Do not canonicalise every filtered page to a parent without checking whether the pages serve distinct search intent. Do not place search-result URLs in the sitemap.

Pagination

Paginated archives should use crawlable links. Each page can self-canonical when it contains a distinct archive slice.

The sitemap does not need every archive page if article URLs are present and the archive remains crawlable. The correct choice depends on architecture and index value.

International and Multilingual Routes

Common patterns include:

/en/services
/hi/services

Use consistent locale identifiers, translated or audience-appropriate slugs and hreflang where relevant. Do not automatically translate URLs without preserving a stable mapping.

Internal-Link Rules

Application links should:

  • use final paths;
  • avoid redirecting variants;
  • use descriptive anchor text;
  • remain crawlable HTML links;
  • avoid constructing URLs with inconsistent helper functions.

Centralise route helpers when many modules create the same path:

export const blogUrl = (slug) => `/blog/${slug}`;

Validate generated links during the build or rendered crawl. The crawlable navigation guide covers discovery.

Sitemap Generation

Generate sitemap URLs from the same records used to create routes:

const baseUrl = "https://www.example.com";

return posts.map((post) => ({
  url: `${baseUrl}/blog/${post.slug}`,
  lastModified: post.lastUpdated ?? post.date,
}));

Reject duplicate slugs before sitemap generation. A sitemap should not contain redirects, non-canonical hosts, noindex pages or missing routes.

Plan URL Migrations Before Changing a Slug

A cleaner-looking path is not automatically worth a migration. Existing URLs may already carry search history, internal links, external links, bookmarks and analytics continuity.

Before changing a public slug:

  1. confirm that the current URL creates a real user or architecture problem;
  2. record every old-to-new mapping in a redirect manifest;
  3. update internal links so they point directly to the destination;
  4. update canonical, Open Graph, schema and sitemap output together;
  5. preserve query strings only when they remain useful and safe;
  6. return one permanent redirect from the old URL to the final URL;
  7. test that no host or trailing-slash rule creates a second hop;
  8. monitor crawl errors, indexed URLs and incoming traffic after release.

Do not reuse an old slug for unrelated content. If a migration changes many routes, deploy the redirect map with the new pages rather than leaving a period where both versions fail.

For an exported Next.js site, validate the generated files and the hosting redirect layer. Application logic can be correct while a CDN, domain or static-host rule still creates a chain.

Keep the old mappings as durable project knowledge. Removing a redirect merely because Google has recrawled the page can break old links that remain in documents, messages or third-party websites.

Validation Checklist

  • [ ] Slugs are unique and stored.
  • [ ] Titles can change without automatic URL changes.
  • [ ] Routes use lowercase and hyphens.
  • [ ] Unknown slugs return not found.
  • [ ] Final host and slash policy are consistent.
  • [ ] Canonical, Open Graph and schema URL match.
  • [ ] Sitemap uses only final URLs.
  • [ ] Internal links bypass redirects.
  • [ ] Old slugs redirect once.
  • [ ] Parameters have documented index rules.
  • [ ] Exported or server-rendered HTML was inspected.
  • [ ] Build checks find broken links.

Current VASUYASHII Evidence

The current VASUYASHII blog stores public slugs in MDX frontmatter, generates static parameters from the content inventory and renders final-www canonical, Open Graph and structured-data URLs from the selected post.

The present build contains 616 blog routes and a 636-URL sitemap using https://www.vasuyashii.com. Batch validation checks duplicate sitemap entries, non-www URLs, broken rendered links and target metadata. This is implementation evidence, not a promise that Google will select every declared canonical immediately.

The automatic sitemap update guide explains inventory-driven generation.

Common Mistakes

  • Regenerating slugs after title edits.
  • Publishing duplicate slugs.
  • Using absolute links with an old host.
  • Adding dates that require annual migrations.
  • Letting query parameters enter the sitemap.
  • Redirecting through several host variants.
  • Canonicalising to a URL that redirects or returns an error.
  • Using fragment routes for primary content.
  • Creating city keyword paths with no distinct value.
  • Checking source metadata but not rendered HTML.

FAQs

Should a blog URL contain the year?

Only when the year is part of the page’s durable identity. Otherwise update the content and metadata without forcing annual slug changes.

Are short URLs always better?

No. A URL should be concise but still descriptive and distinguishable.

Should stop words be removed?

Not mechanically. Remove words only when meaning and readability remain clear.

Can I change a slug after publishing?

Yes, but add a relevant permanent redirect, update all signals and monitor the migration.

Should canonical URLs be relative or absolute?

Use absolute final URLs for canonical metadata.

Does a canonical tag guarantee indexing?

No. It is one signal. Google evaluates redirects, sitemap, internal links, content and other signals.

Next Step

Document the final host, route hierarchy, slug ownership and parameter rules before adding more pages. For a Next.js architecture review, contact VASUYASHII.