Skip to content

Title Tags

A title tag is the text in a page's head that names the page, and it is what search results and browser tabs display. Every page needs exactly one, it should describe that page rather than the site, and no two indexable pages should share it.

What we measured

We ran a title audit across all 216 of our own exported pages. Zero pages are missing a title, zero have more than one, and among indexable pages zero titles are duplicated. The two findings are both non-defects: /404 and /_not-found share a title and are both noindex, which is a framework artifact rather than a mistake, and one indexable title runs 65 characters against a 60 character guideline that is not a limit.

Why it matters

  • The title is usually the first thing a person reads about your page, before they have seen the page at all. It is doing sales work, not just classification.
  • Two pages with the same title are two pages competing to be the same result. Search engines have to pick one, and the choice may not be yours.
  • A template that emits one title for a whole section is the common cause, which means the fix is one change rather than fifty.
  • It is one of the few things you control completely, right up until Google decides to rewrite it.

The failure

The failure: one template title across a whole section
// src/app/services/[slug]/page.tsx
export const metadata = {
  title: "Services | Axion Deep Digital",
};

// Every service page now ships the same title:
//   /services/technical-seo   -> "Services | Axion Deep Digital"
//   /services/web-design      -> "Services | Axion Deep Digital"
//   /services/local-seo       -> "Services | Axion Deep Digital"

A static metadata export on a dynamic route. Nothing errors, every page renders, and every page in the section is now indistinguishable from its siblings in a search result.

The fix

The fix: generate the title from the page's own data
export async function generateMetadata({ params }) {
  const { slug } = await params;
  const service = getService(slug);
  return {
    title: { absolute: `${service.name} | Axion Deep Digital` },
  };
}

Derived rather than declared, which is why our 216 pages have no duplicate titles among indexable pages without anyone maintaining a list. Using absolute here suppresses the layout's template suffix, which is what pushes a good title past the display limit.

Verify the fix

Changing the code is not the same as fixing the problem. Confirm it.

  1. Run the script against your rendered output. Templates decide titles, so the source will not tell you what actually shipped.
  2. Treat a duplicate as a finding only when more than one indexable page shares the title. Noindex pages cannot compete with each other.
  3. For anything flagged LONG, look at the title before changing it. 60 characters is a display guideline, not a limit, and a clear 65 character title beats a truncated-sounding 58 character one.
  4. Check that each title describes its own page rather than the site. A title that is unique and still says nothing passes the script and fails the reader.
  5. Compare against what Google actually displays, using Search Console or a site: query. Google rewrites titles routinely, and a rewrite is a signal your title did not match the query as well as the page did.
Run this against your own rendered pages
import re, glob, collections

titles = collections.defaultdict(list)

for f in sorted(glob.glob("out/**/*.html", recursive=True)):
    html = open(f, encoding="utf-8", errors="ignore").read()
    if "<html" not in html:
        continue
    path = "/" + f[len("out/"):].replace(".html", "")
    noindex = 'content="noindex' in html
    found = re.findall(r"<title>(.*?)</title>", html, re.S)

    if not found:
        print("NO TITLE", path, "(noindex)" if noindex else "(INDEXABLE)")
        continue
    if len(found) > 1:
        print("MULTIPLE TITLES", path, len(found))

    title = found[0].replace("&amp;", "&").strip()
    titles[title].append((path, noindex))
    if len(title) > 60:
        print("LONG", len(title), path, "(noindex)" if noindex else "(INDEXABLE)")

for title, pages in titles.items():
    indexable = [p for p, ni in pages if not ni]
    if len(indexable) > 1:
        print("DUPLICATE across", len(indexable), "indexable pages:", title)
        for p in indexable:
            print("   ", p)

It only reports a duplicate when more than one INDEXABLE page shares the title. That single condition is what turned our two raw findings into zero defects, and it is the difference between an audit you can act on and a list you have to triage by hand.

Exceptions and misconceptions

60 characters is a guideline, not a limit

Search results truncate by pixel width, not character count, so the real limit depends on the characters you used. One of our indexable titles runs 65 characters and we left it alone, because it reads correctly and shortening it would cost more meaning than the truncation risk is worth.

Google rewrites titles, and that is not a failure

Google may replace your title with page text it considers a better match for the query. You cannot prevent it, and chasing it is wasted effort. Write the title for the person, not for the guarantee.

A duplicate on noindex pages is usually a framework artifact

Our /404 and /_not-found pages share a title because the framework emits both. Neither is indexable, so neither can compete with anything. An audit tool counting this as a duplicate title issue is counting a non-problem.

The title is not the H1

The title lives in the head and is what search results show. The H1 lives on the page. They should agree, but they serve different readers and do not have to be identical.

Watch it

Primary sources

The measurement above comes from our study, State of Small Business Websites 2026.

Related lessons

Check your own site for this

DeepAudit AI renders your page in a real browser and reports the affected code, so you can see exactly where each finding came from. Free, no signup.

Run a free audit

Last reviewed 2026-09-17. Checks covered: Title Tag, Multiple Title Tags, Title Duplicates.