How Docsbook builds the head of a page
This page is the mechanism: what Docsbook puts in the <head> and in sitemap.xml
for every page it hosts, in the order the code resolves it, so you can predict the
output instead of curling it. For what it is worth to you and what you have to switch
on, start at the SEO index.
What is the title on the page, and where does it come from?#
The <title> of a Docsbook page is resolved in three steps, first match wins:
| Order | Source | Why it is first |
|---|---|---|
| 1 | Frontmatter title: |
The only one of the three you can edit without changing what a reader sees on the page. |
| 2 | The body # H1 |
A real heading, already written for a human. |
| 3 | A title derived from the file name | Never empty; a page always has a SERP line. |
The workspace name is then appended exactly once, as Page title — Workspace,
and skipped when the title already says it as a standalone word. "Docs" inside
"Docsbook" does not count — both neighbours of the match must be non-word characters,
tested against Unicode letters and digits rather than ASCII ones, so a Cyrillic or
CJK workspace name matches the same way a Latin one does. A page whose title is only
the workspace name (the site root) becomes Workspace — Documentation. The finished
string is emitted as an absolute title, which stops the site-wide %s | Docsbook
template from appending a second brand copy.
On a translated page the title comes from the cached translated metadata, and
otherwise from the first <h1> of the stored translated HTML — so a Chinese page
ships a Chinese title. The description is deliberately left in the source language
there: Docsbook does not invent a translation for it.
What is the meta description, and what is stripped out of it?#
Order: frontmatter description: first, then the page's own opening paragraphs.
Before body text can become a description it is cleaned: HTML comments (which is what
widget markers are), {icon-name} markers, headings, images, fenced and inline code,
emphasis characters, raw HTML tags, list bullets and blockquote markers are removed,
and a markdown link collapses to its link text rather than dragging its URL into the
sentence. Paragraphs of 20 characters or fewer are dropped as fragments.
Two lengths are built from the same source in one pass: 160 characters for
<meta name="description"> and 400 for og:description and the JSON-LD
description. An authored frontmatter description fills both. Truncation lands on a
word boundary, and prefers the end of a sentence when one falls in the back half of
the budget; otherwise the text ends in an ellipsis.
Which URL does the page call canonical?#
One page, one canonical URL, resolved in this order:
- Your custom domain, when the workspace has one. The
*.docsbook.iomirror then servesDisallow: /rather than standing as a second copy. - A product-owned apex path, for Docsbook's own documentation.
- The apex short path for showcase workspaces, because that is the URL that
answers
200— the subdomain form redirects to it. https://<owner>.docsbook.io/<repo>/<path>for everything else.
Translated pages take the same four branches with the locale inserted where the
router actually serves it. en is special-cased back to the unprefixed URL, since
/en/page and /page serve byte-identical content, and a locale URL for a page that
is not actually translated renders the source text, so it canonicals to the
source URL instead of claiming to be authoritative.
Which languages are advertised as alternates?#
The hreflang set contains x-default and en at the source URL, plus one entry
per enabled language that this page has actually been translated into. Enabling a
language does not add it: an untranslated locale URL canonicals away from itself, and
one such member is enough to void the whole cluster. A page carrying noindex gets
no set at all, rather than a dangling one.
The sitemap emits no page-level alternates, on purpose: it cannot afford the per-page translation check, so any set it built would list every enabled locale and reintroduce exactly the contradiction the page-level set exists to avoid.
What do the social cards contain?#
Every page emits OpenGraph (og:title, og:description at the 400-character length,
og:url = the canonical URL, og:site_name, og:type: article, og:locale) and an
X card of type summary_large_image carrying the 160-character description. The
image is generated per page at 1200×630, cached for 24 hours, and renders the
workspace lockup, the section as an eyebrow, the page title (cut at 64 characters,
set smaller above 30) and the description cut at 130, in the workspace's colours. On a
custom domain the card is the same image, requested by absolute URL from the apex — but
og:description there carries the 160-character string, not the 400-character one.
What robots directives does a page carry?#
Four rules, in strict precedence:
| Condition | Emitted |
|---|---|
Admin preview (?preview=true) |
noindex, follow |
| Site-wide SEO switch off | noindex, nofollow |
Page frontmatter noindex |
noindex, follow |
| Otherwise | index, follow |
noindex: true, noindex: yes, noindex: 1 and the robots: noindex spelling all
count. Anything else — absent, false, index — means index.
robots.txt differs by host. The apex serves a permissive wildcard rule with
Crawl-delay: 10, disallows the app's own non-content paths, names eighteen AI
and search crawlers explicitly at Crawl-delay: 5, blocks thirteen high-volume
low-citation-value crawlers outright, and lists one Sitemap: line per discoverable site. A
workspace subdomain serves the same bot policy plus its own Sitemap: line. A custom
domain serves the bot policy with no Sitemap: line — it has no sitemap of its
own yet, and pointing crawlers at the mirror's sitemap would advertise a second host
for every page. Crawl-delay is a courtesy, not a standard: RFC 9309
defines only user-agent, allow and disallow, and
Google
adds sitemap and nothing else — "other fields such as crawl-delay aren't supported".
What goes into sitemap.xml?#
One sitemap per owner, rebuilt at most hourly. For each indexed repository it lists
every Markdown file, mapping a repository-root README to the site root and every
other file to its own path. Each entry carries:
lastmod— the date of the last commit that touched that file, from the source repository. Render time is used only when the commit history cannot be read.changefreq—weekly.priority—0.9for a landing page,0.7for an inner page, and0.8/0.6for their translations.
Translated URLs are listed only where a translation genuinely exists, and duplicate URLs are collapsed before the file is emitted. A repository whose tree cannot be read is dropped silently and the rest of the sitemap is still served: a sitemap that 500s costs more than one that is a site short.
Pages carrying noindex are still listed. Recognising the flag means reading
every page's content, which building the sitemap deliberately does not do; the page's
own directive is honoured on arrival, so the cost is one crawl visit.
What structured data is emitted?#
On a Docsbook-hosted host, every page emits a JSON-LD @graph with three nodes:
Organization— the workspace, its URL, its GitHub profile, its logo if set.TechArticle— headline, description, canonical URL,inLanguage,datePublishedanddateModifiedfrom the source repository's commit history, author, publisher,mainEntityOfPage.BreadcrumbList— owner → site → each path segment, built from the same canonical builder the<link rel="canonical">uses, so no crumb can name a host the canonical tag disagrees with.
With AEO on, speakable is added, and FAQPage / HowTo nodes
appear only when the page genuinely contains that shape. With
GEO on, a Person author is added from frontmatter or from the
last commit's author.
Anchors, render mode and hosts#
Anchors. Heading ids come from the renderer's own slugger, and every deep link Docsbook hands out — search results, AI citations — is computed by calling that same library rather than re-deriving the string. Duplicate headings resolve to the first occurrence.
Render mode. An anonymous request for a public page is served from a cached server-rendered route (24-hour window); signed-in and preview requests fall through to a dynamic render and are never CDN-cached. Either way the crawler receives complete HTML — no client-side render step stands between a bot and your text.
Custom domain versus the shared domain. On a custom domain the canonical URL,
title, description, cards and a TechArticle node are all present, and the bot
policy is enforced. Five things are not: the site-wide SEO switch and per-page noindex
(pages are served index, follow unconditionally), the hreflang set, the
BreadcrumbList and Organization nodes, moved-page redirects, and the
GEO signals — no TL;DR block, no visible Updated line, and a
TechArticle author that is always a Person named after the repository owner. See
Limits.
Why these rules (evidence)#
| Rule | Why it works on the consumer | Source |
|---|---|---|
A <title> on every page, brand appended once |
Google lists <title> first among title-link sources, and warns against "repeated or boilerplate text in <title> elements" |
Title links |
| Per-page descriptions, never one site-wide string | "Identical or similar descriptions on every page of a site aren't helpful" | Snippets |
| Canonical points at a URL that answers 200, never a redirect | rel="canonical" is "a strong signal", and Google recommends a self-referential canonical on the canonical page |
Consolidate duplicate URLs |
Only genuinely translated locales in hreflang |
"If page X links to page Y, page Y must link back to page X… those annotations may be ignored" | Localized versions |
Real commit dates as lastmod |
Google uses <lastmod> "if it's consistently and verifiably… accurate" |
Build a sitemap |
| Structured data only for content the page has | "don't add structured data about information that is not visible to the user" | Intro to structured data |
| Server-rendered HTML rather than client-side | Google renders JS in a queue where a page "may stay… for a few seconds, but it can take longer", and "not all bots can run JavaScript" | JavaScript SEO basics |
| 1200×630 card image | "Use images that are at least 1200 x 630 pixels", close to a 1.91:1 ratio | Sharing images |
Limits and open questions#
priorityandchangefreqare decoration. Docsbook emits them, and Google states plainly: "Google ignores<priority>and<changefreq>values." The sitemaps.org protocol adds that priority "is not likely to influence the position of your URLs". They cost nothing and buy nothing from Google; other engines vary.TechArticleis not on Google's Article rich-result list. It is a real schema.org type (Thing > CreativeWork > Article > TechArticle) and describes the content honestly, but Google's Article documentation says objects "must be based on one of the following schema.org types:Article,NewsArticle,BlogPosting". Treat the node as accurate description, not rich-result eligibility. Nor is structured data documented as a ranking factor: Google's introduction describes it as making a page eligible for enhanced appearance and says nothing about rank.- Under question: what the 400-character
og:descriptionbuys. Docsbook builds it because the tag has room where<meta description>does not. No source we fetched documents how any specific consumer truncatesog:description, and the OpenGraph protocol specifies no length. Treat 400 as a house choice, not a measured optimum. - Custom-domain pages ignore your indexing switches. The site-wide SEO switch and
per-page
noindexare honoured only on Docsbook-hosted hosts; on a custom domain the page is servedindex, followregardless./sitemap.xmldoes not resolve there either, so itsrobots.txtcarries noSitemap:line, and a page renamed through Docsbook keeps its redirect only on the shared domain. To keep a page out of the index on a custom domain today, keep it out of the published repository. - A single sitemap caps at 50,000 URLs / 50 MB per the sitemaps.org protocol and Google's own limit. Docsbook emits one sitemap per owner and does not shard; an owner past that ceiling is not handled today.