Updated 2026-10-21

Duplicate titles and meta descriptions on a docs site

Docs sites grow by copying pages. A new section starts as a copy of an old one, every product area gets an "Overview" and a "Configuration" page, and the framework fills in whatever the front matter leaves out. The result is dozens of pages that tell search engines the same thing in their <title> and <meta name="description">.

Google asks for a distinct description per page and descriptive, concise titles (Google Search Central: snippets, title links). When fifty pages share one description, Google writes its own snippet from the page text, and search results for your docs look alike, so people have a harder time picking the right page.

Where the duplicates come from

Docusaurus

Each doc's description comes from its description front matter. Without it, Docusaurus uses the first line of Markdown content as the description (docs front matter). It skips imports, code blocks and the # title, but not everything else, so these patterns produce repeats:

Titles come from title front matter, then the Markdown # heading, then the doc id, followed by the site title. Two pages called "Configuration" in different sections become two identical "Configuration | My Project" titles.

Starlight

Every page has a required title; description is optional. The site-wide description in your Starlight config is used for every page that doesn't set its own (Starlight configuration). So the more pages you have without front matter descriptions, the more pages share the site description.

VitePress

The description in .vitepress/config renders on every page, and a page's front matter description overrides it (VitePress site config). A page's title is its front matter title or the text of its first <h1>, plus the titleTemplate suffix. The same failure modes as Starlight: one description everywhere, and repeated headings become repeated titles.

Find them in your build

Check the HTML your framework produced, not the source files: that's what crawlers read, and it includes the fallbacks. Build first, then point these at the output folder (build for Docusaurus, dist for Starlight, .vitepress/dist for VitePress).

Descriptions used on more than one page, with a count:

grep -rhoE --include='*.html' '<meta name="description" content="[^"]*"' build | sort | uniq -cd | sort -rn

Titles used on more than one page:

grep -rhoE --include='*.html' '<title[^>]*>[^<]*' build | sed 's/<title[^>]*>//' | sort | uniq -cd | sort -rn

To see which files share a given description:

grep -rlF --include='*.html' 'content="Overview"' build

Attribute order and quoting vary between frameworks; if a command prints nothing, open one built page and adjust the pattern to match its tag. For the live site, the free site checker reads your sitemap and lists every duplicated title and description with the pages that share it, across up to 50 pages. For a single live page, the free SEO checker shows the title and description exactly as crawlers get them.

Fix them per framework

Docusaurus

Set both fields in the front matter of each doc. description also becomes og:description.

---
title: Configure the CLI
description: Every option in headlark.config.json, with defaults and an example for monorepos.
sidebar_label: Configuration
---

title is for the browser tab and search results; sidebar_label keeps the short name in the sidebar. For old versions you don't want in search results, set noIndex on the version in the docs plugin options:

// docusaurus.config.js, inside the docs plugin or preset options
docs: {
  versions: {
    '1.x': { noIndex: true },
  },
},

Starlight

---
title: Configure the CLI
description: Every option in headlark.config.json, with defaults and an example for monorepos.
---

Keep the site-wide description in astro.config.mjs for the home page, and treat any other page that still shows it as unfinished. Starlight joins the page and site titles with titleDelimiter (default |); the page title is the part that has to be unique.

VitePress

---
title: Configure the CLI
description: Every option in headlark.config.json, with defaults and an example for monorepos.
---

To catch pages that still inherit the site description, a transformPageData hook in .vitepress/config.mts can list them during the build:

export default defineConfig({
  description: 'Docs for the example CLI',
  transformPageData(pageData) {
    if (!pageData.frontmatter.description) {
      console.warn(`no description: ${pageData.relativePath}`)
    }
  },
})

Writing ones that stay unique

Automating it with Headlark

Writing and maintaining a description for every page is the part that slips on a large docs site. Headlark's build step runs after your framework's build, on the HTML in build, dist or .vitepress/dist:

"build": "docusaurus build && headlark build --site-url https://docs.example.com"

Review the results on your most visited pages; it takes minutes when the rest is done.

Try it

The free plan covers one site and 25 pages. Add your site, copy its secret key, and your next build ships with SEO.