Most on-page advice still assumes the page is the unit of retrieval. It frequently isn't. Google announced passage ranking in 2020, describing it as the ability to identify and rank individual passages within a page rather than judging the page as a single block. Every retrieval-augmented generation system built since then does something structurally similar: it splits your HTML into chunks, embeds each chunk separately, and retrieves the chunks — not the document.
That means a boundary you never chose, inserted by a text splitter you've never seen, determines what a model actually reads about your topic. A paragraph that makes perfect sense after three paragraphs of setup can be useless on its own. This is a mechanical problem with a mechanical fix, and you can test it on your own pages today.
Why a chunk, not a page, is the thing being scored
The constraint is context windows and embedding quality. Embedding a 4,000-word page into one vector averages away everything specific; the vector ends up near the centroid of the topic and far from any particular question. Shorter chunks produce sharper vectors. So retrieval pipelines split first, embed second.
Google has documented passage ranking as a real system and said at launch it would affect a meaningful share of queries across languages. What Google has not published is how it segments pages — chunk sizes, overlap, whether segmentation is heading-aware. Treat any specific number you see quoted for Google as inference, not documentation.
What is observable: Google links to specific passages using scroll-to-text fragments (#:~:text=) in featured snippets, which only makes sense if a sub-document span was the retrieval target. And open-source RAG stacks, which handle a growing share of AI-assistant citations through crawler-fed indexes, are explicit about their splitters. Those are the mechanics you can reason about directly.
How a typical chunker splits your HTML
The generic pipeline is: fetch, strip to text, split, embed.
- Boilerplate removal. Readability-style extraction tries to find the main content block. It often keeps nav text, cookie notices, or related-post lists when the markup is ambiguous.
- Markdown or plain-text conversion. Tables get flattened. Definition lists lose structure. Images become alt text or vanish.
- Recursive splitting. The common default tries separators in order — double newline, single newline, sentence, space — until each piece fits a token budget, typically a few hundred tokens, often with 10–20% overlap between adjacent chunks.
- Embedding per chunk, with the chunk stored alongside its source URL.
Step 3 is where your editorial structure either survives or doesn't. A recursive splitter respects paragraph breaks but knows nothing about whether a paragraph depends on the one before it.
| Splitting strategy | Boundary placed at | Main failure risk |
|---|---|---|
| Fixed token window | Arbitrary token count | Cuts mid-sentence, mid-table |
| Recursive character | Nearest paragraph break | Orphans dependent paragraphs |
| Heading-aware | h2/h3 boundaries | Over-long sections exceed budget |
| Semantic (embedding drift) | Topic shift detected | Unstable, costly, rare in practice |
If your content survives arbitrary token-window splitting, it survives everything else. That's the test to design for.
The four things that break at a seam
Dangling references. A chunk that opens with "This approach fails when…" has lost its antecedent. The embedding now represents a vague complaint rather than a claim about a named thing. Retrieval for the entity you care about will skip it.
Orphaned table rows. Flattening converts a table to lines of text. If the header row lands at the end of chunk 3 and the data rows in chunk 4, the numbers arrive unlabeled. This is the most common silent loss in specification and pricing content.
Definition–detail separation. You define the term once in the intro, then discuss it for 2,000 words. Chunks 2 through 9 contain detail with no definitional anchor, so a query phrased in the definitional language matches only chunk 1 — which contains no detail.
Boilerplate dilution. A chunk that is 40% navigation text has a vector pulled toward "site menu" semantics. Short pages with heavy chrome suffer most.
How to simulate the split on your own pages
Don't theorize. Run your own splitter and read the output.
import re, tiktoken
enc = tiktoken.get_encoding("cl100k_base")
def chunks(text, max_tokens=350, overlap=50):
paras, buf, out = re.split(r"\n\s*\n", text), [], []
for p in paras:
buf.append(p)
if len(enc.encode("\n\n".join(buf))) > max_tokens:
out.append("\n\n".join(buf))
buf = buf[-1:] if overlap else []
if buf: out.append("\n\n".join(buf))
return out
Feed it the extracted text, not your CMS source — use a readability extractor first so you see the same boilerplate the crawler keeps. Then do two checks:
- The standalone read. Print each chunk and ask: could a reader who has seen nothing else answer a question from this? If a chunk opens with a pronoun or an unnamed metric, it fails.
- The retrieval check. Embed the chunks and embed five real queries from your Search Console data for that page. Rank chunks by similarity per query. If the top chunk for your most important query is the intro boilerplate or a related-posts list, your page body is not what gets read.
Relative ranking of chunks within one page is the useful output here. Absolute similarity scores are not comparable across models or corpora.
Writing passages that survive being cut out
The fix is repetition of entities, not repetition of explanation.
- Name the subject in the first sentence of every section. Replace "This fails when traffic spikes" with "Edge caching fails when traffic spikes." One noun phrase, no added length.
- Keep tables short and label units inline. A table under ~10 rows usually fits a single chunk. For long specs, repeat the header or put units in each cell.
- Front-load each H2 with the answer. The first 50 words of a section carry disproportionate weight because they appear in both the section's own chunk and the overlap of the previous one.
- Give every H2 a stable
id. It costs nothing and gives citing systems a deep-link target. - Cut interstitial chrome inside the article body. Mid-article CTAs and related-post widgets land inside content chunks and dilute them.
What this is not a license for: restating full definitions in every section. That bloats the page and the dilution hurts the chunk that mattered. One naming clause per section is the dose.
What to do this week
- Pick your five highest-value pages and run them through an extractor plus the splitter above at a 350-token budget.
- Read every chunk cold. Flag any that open with an unresolved reference or contain unlabeled numbers.
- Fix the openings of flagged sections with an explicit subject; fix orphaned tables by shortening or re-labeling.
- Re-run the retrieval check and confirm the winning chunk for your top query is substantive content, not chrome.
- Add the standalone read to your editorial checklist so new sections ship self-contained.
You cannot observe Google's segmentation directly, and you should not claim to. You can make your content robust to any reasonable segmentation, which is the only version of this problem you control.