Page content, near-duplicates and conversion paths

How SEOFix reads a page's main content, flags template-heavy and near-duplicate pages, and finds traffic pages that don't lead to a signup, pricing or app page.

Updated 20 October 2026View as Markdown

SEOFix reads the main content of each page, not the menu or footer. From that it finds pages that are mostly template text, pages that nearly duplicate another page, and pages that get Google clicks but don't link to a money page (signup, pricing, an app store) from their content. It measures page content and links only. It does not measure signups or revenue.

How we read page content

For each crawled page the crawler picks one container and counts the words in it:

  1. The first <main> element, if it holds at least 20 words once its own chrome is removed: <header>, <nav>, <footer> and <aside> elements, and elements with a navigation, banner, contentinfo, complementary or search role, inside <main>.
  2. Otherwise the only <article> on the page, if there is exactly one.
  3. Otherwise the <body>, with page chrome removed: <header>, <nav>, <footer>, <aside>, elements with a navigation, banner, contentinfo, complementary or search role, and elements whose id or class names a nav, navbar, navigation, menu, topbar, masthead, footer, header, sidebar, cookie, breadcrumb, banner, newsletter or share block.

An element is never removed for its id or class name when it contains the page's first <h1>, or holds more than half of the container's words. A layout wrapper such as <div class="layout has-sidebar"> around the whole article is kept, and only the real sidebar inside it is removed. The same holds for chrome inside <main>: a <header> that carries the article's <h1> stays.

Scripts, styles, noscript, template and svg are always ignored. The text is split into blocks (paragraphs, list items, headings, table cells and similar). Blocks under 4 words are counted as words but never compared. At most 400 blocks per page are compared. A page also stores its H2 and H3 headings (at most 30).

Template-heavy pages

TEMPLATE_HEAVY_PAGE (notice) marks a page whose main content is mostly text repeated across its URL template. A block counts as template text when it appears on at least half of the pages of that template. The template share is computed over the compared blocks only: blocks under 4 words, and blocks past the 400 limit, count neither as template nor as the page's own text.

A page is flagged when all of these hold:

Rule Value
Template has measured pages at least 5
Compared words on the page at least 100
Template share 70% or more
The page's own words under 250

Only indexable pages that answer 200 are measured. In Search Console triage, a "Crawled - currently not indexed" or "Soft 404" group where at least half the pages have this signal gets the action "Only N of the M words we compared are their own; the rest repeats on every /template page. …", with the group's median own words and median compared words. The check is re-evaluated at the next full audit, not by Verify fix.

Where to see a page's figures

Every measured page has three figures: main_words (words in its main content), own_words (words in compared blocks that are not template text) and template_share (0-1, the share of its compared words that are template text). own_words and template_share are null when the page was not measured (a template with under 5 measured pages, or a page that is not indexable).

  • In the app, open a TEMPLATE_HEAVY_PAGE issue: each URL shows its template share, own words, compared words and main words.
  • API: GET /v1/sites/{id}/google/url?url=… returns them as crawl.content, and each row of GET /v1/crawls/{id}/pages has content. Both are null for a page without main-content data (not HTML, or an audit made before main-content extraction). The MCP tool get_url_status returns the same crawl.content.

Near-duplicates

NEAR_DUPLICATE_CONTENT (warning) marks two or more indexable pages whose main content is nearly the same but not identical. Identical content is DUPLICATE_CONTENT. Each page needs at least 100 main-content words. The task lists up to 3 twins per page, nearest first.

These pairs are not reported: pages with the same content hash, pages that declare each other as hreflang alternates, and pages under two different locale prefixes (/en/x and /de/x, treated as translations). A fingerprint section shared by more than 200 pages is not used for comparing (boilerplate-only pages); those pages are still compared through their other sections. See Indexability checks.

In not-indexed triage, a not-indexed page whose near-duplicate twin Google indexes is in the Duplicates class with the action "Merge into the twin page".

Conversion paths

On the audit page, "Turn traffic into signups" lists pages that get Google clicks but don't link to a money page (a signup, pricing or app page we recognise) from their main content. Each group becomes a fix task with code CONVERSION_GAP and kind: "conversion".

What counts as a money page

A link target is a money page when it is:

  • A page on your registrable domain (any subdomain, such as app.example.com) with a whole path segment equal to one of: register, signup, sign-up, join, get-started, start, trial, free-trial, pricing, plans, plan, checkout, cart, buy, order, subscribe, demo, book, booking, quote, contact, contact-us, download, apply. Matching is case-insensitive and on whole segments: /pricing/team matches, /pricing-guide does not.
  • A page on your domain with a path segment starting with get-a- (for example /get-a-quote).
  • A link to apps.apple.com, itunes.apple.com or play.google.com, with any path.
  • A page on your domain whose path starts with one of your own conversion paths, or a URL under one of your own conversion URLs (below).
  • A link to a URL on your site that the audit found redirecting, classified by where it ends up (up to 5 hops). A link to /ios that redirects to apps.apple.com counts as a link to the App Store.

A money page itself never has a gap.

These are never detected as money links:

  • Forms, such as a signup or lead form in the page itself.
  • Phone links (tel:), email links (mailto:) and WhatsApp links (wa.me, api.whatsapp.com).
  • Pages on another site, unless you add them as conversion URLs: an external job application page, a booking tool or a shop on another domain.

If your pages convert through one of these, a missing gap can be a false alarm. Add the page as a conversion URL when it is a link, or mark the task done.

Your own conversion paths

In the app: Site, Conversion pages (one per line). Agents: PATCH /v1/sites/{id} with conversion_paths. Each entry is one of:

  • A path on your site, starting with / and more than just /. It matches itself and everything under it, on whole segments: /topup matches /topup and /topup/plans, not /topups-guide.
  • A full http or https URL, such as https://apply.workable.com (that host and every page on it) or https://book.example.com/acme (that path and everything under it on that host). A leading www. is ignored on both sides. Use it for apply, booking or checkout pages on another site.

These add to the defaults; they do not replace them. At most 20 entries, each up to 200 characters. Send [] to clear them.

What is measured

SEOFix looks at every indexable page that is not a money page and has 30 or more Google clicks in 28 days (Search Console data needed). It finds the first link to a money page inside the main content, in words of main content before the link:

Gap When
missing ("no link in content") No money link in the main content
late ("first link after N words") The first money link comes after 300 words

A page where the crawler saw no readable main content, with no main-content data or fewer than 50 main-content words (such as an app shell that renders in the browser), is left out: it is never reported as missing, and it is not counted in the templates' shares. The traffic summary counts these pages in pages_unknown.

Pages are grouped per URL template and gap type. A link to a money page in the menu or footer does not count as in-content, but it is reported: "Your menu links to the App Store, /register" tells you the target exists and readers of the content don't see it. An in-content link whose position is unknown counts as position 0 (never late).

Conversion tasks have no click estimate (est_clicks_28d and value_28d are null), are not part of the Top 3 and do not count toward clicks at risk or available. Without Search Console data the card shows, per template, the share of indexable non-money pages with no in-content money link. Audits made before link positions were stored show no conversion data at all.

What is not measured

SEOFix does not know how many visitors sign up or buy. There are no conversion rates, and no estimate of the signups you would gain. It reports where a link to a money page is missing or late in content that already gets Google traffic.

Marking done and regressed

A recheck cannot see this, so you mark the task done by hand: the button in the app, or POST /v1/sites/{id}/tasks/{key}/status with {"status": "done"} (the MCP server has no tool for this: call the API). If a full audit that started after you marked it still finds pages of the same template and gap type, the task reads regressed (open again) at the next read. The next full audit is the check.

More in Fixes

Still stuck? Email hello@seofix.ai with your site and what you expected to see.