With the SEO backend unreachable since start, /robots.txt and /sitemap.xml
answered 503 where v0.1.1 left them to the app (a consumer's smoke expects
the app's 404). The root proxies now need a site answer; the last one seen
is kept while the backend is down, so a failure after it is still 503.
- server/middleware/seo-root.ts proxies /robots.txt, /sitemap.xml,
/sitemap-<name>.xml, /llms.txt and the verification and IndexNow key
files the site answer lists in root_files, with the backend's status,
content type and X-Robots-Tag; a backend failure is 503 Retry-After 60
- server/lib/site.ts caches GET /_seo/api/v1/site (300 s, cleared by the
purge); a backend without it (404) keeps the v0.1.1 behaviour: no root
proxies, the verification lookup by name
- the 404 report and the verification lookup honour seo.cms and rootFiles
- smoke runs for the layer off (seo.cms and the v0.1.1 enabled switch), the
CMS down (fail open) and no 404 token; npm run purge:check
- the Nitro middleware answers redirect (301/302, Location from the answer
only, same-site targets as paths as in v0.1.1) and gone (410) before the
render and the route cache, sets X-Robots-Tag, and answers 404 for a
pagination.invalid answer; other not_found answers stay with Nuxt
- resolve answers cached in an LRU (seo.resolveCacheTtl, default 60 s,
1000 entries) with in-flight de-duplication; a clear detaches loads in
flight so no answer fetched before a purge is stored after it
- the playground route-caches /blog/** (swr 300)
- Nitro middleware resolves page requests once and hands the answer to the
render (event.context.seo); the route middleware reuses it
- useCmsSeo: html lang, hreflang links, og:locale and exactly one JSON-LD
script with one @graph (CMS nodes plus useSchemaNode nodes, merged by @id)
- local module (configKey seo): cms (alias of public.seo.enabled),
breadcrumbs, links.newTabHint, rootFiles, resolveFiles
- GET /api/__seo__/resolve for client navigation through the Nuxt server
- smoke harness: count=, exact header=, noheader=, reqheader=, repeated
options, resolves/apicalls/noreport lines, --no-site; v0.1.1 suite runs
unchanged against a backend without /site