Repository navigation
feat(seo): put the rendered page into #root for search-engine crawlers - #1796
Merged
Merged
Conversation
A shell contributor behind PublicOnly, after the page meta contributor, puts <article><h1>title</h1>, the page rendered by internal/render and <nav> links to the page's direct children into #root when the user agent is a search engine (googlebot, google-inspectiontool, bingbot, duckduckbot, yandex, baiduspider, applebot). Browsers, unknown pages, the root and /p/ permalinks get the plain shell. The seo adapter reads through two new pages use cases: ListChildRoutesUseCase on TreeService.ChildRoutes (one locked read) and ResolveWikiLinkUseCase on the now exported links.ResolveWikiLinkTarget, which applies the rule stored links follow. Neither reads a page file. Renders are cached per page version (base path, route, content, while every wikilink still resolves the same) in a 16 MB LRU cache, and at most two run at once; beyond that, or when the client has gone away, the plain shell is served without a render. A render failure is never a 500: it is cached and logged once, without page content. ADR-0020 is accepted. Behavior-Change: every shell of a public wiki now carries Vary: User-Agent and Cache-Control: no-cache; search-engine crawlers get the rendered page inside #root.
…rrency More user agents (mobile Bingbot, Googlebot-Image, mixed case, spoofed and other bots), a deleted and recreated wikilink target in the cache table, a burst of first requests for one page against the render cap, hostile and Unicode content, and on the real wiki: the cached body against a fresh render after edits, moves, renames and child changes, a crawler asking for Markdown, a too-complex page and crawler requests during tree changes. TestWiki_CrawlerBody_ConcurrentWithTreeChanges runs crawler requests while pages are moved and renamed; under -race it pins that wikilink and child route paths are read under the tree lock.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Last step of the SEO series for #1292 (after #1795, which added the shell
Varyseam and the locked route reads). Step "rendered body for search-engine crawlers" from ADR-0018.Search engines that don't run JavaScript (and Google's first pass) see an empty
#root. Public wikis now put the page into#rootfor known crawlers; the SPA'screateRootreplaces it, so browsers are unaffected.What's in it
seo/crawler_body.go:isSearchEngineCrawler(case-insensitive substrings googlebot, bingbot, duckduckbot, yandex, baiduspider, applebot, google-inspectiontool) andGetCrawlerBodyUseCase:<article><h1>escaped title</h1>+render.RenderHTML(ADR-0020 renderer and limits, on main since feat(render): render sanitized page HTML for crawlers #1789/fix(render): bound render cost and fail closed outside the policy #1792) +<nav><ul>of escaped child links.PublicOnly.Contributor, after the page meta contributor. Every public shell getsVary: User-Agentandno-cache.ErrTooComplex→ plain shell, Warn once per page version / once per process, never page content in the log./p/permalinks, the root, unknown pages or app screens; a crawler that asks fortext/markdowngets the Markdown.ListChildRoutesUseCase(newTreeService.ChildRoutes, locked) andResolveWikiLinkUseCase(exportedlinks.ResolveWikiLinkTargetover the locked route reads).seo-crawler-body.spec.ts: a Googlebot UA gets the article in#root, the SPA replaces it (content once, at most one H1); a browser UA gets an empty#root.Behavior-Change: in public wikis, requests from search-engine crawlers get the rendered page in
#root; every public shell response carriesVary: User-Agent.Checks
make test,make lint, deadcode,make verify,make cross,make verify-commitsgreen;-raceon seo, pages, tree, links and the real-router crawler tests (fresh frontend build); e2e crawler + meta specs pass.For your review
yandexsubstring breadth, the cap of 2 renders and the 16 MB cache are not measured on a Raspberry Pi yet.Vary: User-Agentdisables shared caching of the shell (it isno-cacheanyway).curl -A 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'against a deployed public wiki (proxy/CDN Vary handling), then Search Console URL Inspection (live test usesGoogle-InspectionTool, included).🤖 Generated with Claude Code