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.
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:
- 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>. - Otherwise the only
<article>on the page, if there is exactly one. - 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_PAGEissue: each URL shows its template share, own words, compared words and main words. - API:
GET /v1/sites/{id}/google/url?url=…returns them ascrawl.content, and each row ofGET /v1/crawls/{id}/pageshascontent. Both arenullfor a page without main-content data (not HTML, or an audit made before main-content extraction). The MCP toolget_url_statusreturns the samecrawl.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/teammatches,/pricing-guidedoes 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.comorplay.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
/iosthat redirects toapps.apple.comcounts 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:/topupmatches/topupand/topup/plans, not/topups-guide. - A full
httporhttpsURL, such ashttps://apply.workable.com(that host and every page on it) orhttps://book.example.com/acme(that path and everything under it on that host). A leadingwww.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.
Related
More in Fixes
Still stuck? Email hello@seofix.ai with your site and what you expected to see.