Serving your site as Markdown to LLMs and agents
Every page in Turbo Start Sanity is also available as clean Markdown. Here is exactly how the content-negotiation layer works, from proxy to serializer.
Agents do not want your HTML. They want the words. When an LLM crawls a normal page it burns tokens parsing navigation, wrappers and script tags to recover a few paragraphs of meaning. Turbo Start Sanity sidesteps that entirely: every page is also served as clean Markdown, from the same content, with no separate export step.
Two ways to ask for it
There are two triggers. Append .md to any URL — /about.md, /blog/some-post.md, /index.md — or send an Accept: text/markdown header. Either one gets you Markdown; everything else gets the normal page.
The proxy decides
A proxy inspects each GET or HEAD request. If the path ends in .md or the Accept header prefers Markdown, it strips the suffix, normalises the path, and rewrites the request to an internal route handler — forwarding the resolved content path as a request header, because a rewrite's query params are not reliably visible downstream:
There is a deliberate guard: a header-negotiated request whose last path segment contains a dot — an asset like a .png sent with a broad Accept — passes straight through, so you never try to Markdown-render an image.
The route builds the Markdown
The route handler reads the path, splits it into segments, and fetches the matching Sanity data — home, blog index, blog post or generic page — each wrapped in "use cache" so the underlying sanityFetch participates in tag-based revalidation. It always fetches the published perspective with stega disabled, because this output is for machines.
The serialization is the interesting part. It never touches React. A thin dispatcher walks the page-builder array and maps each block's _type to a co-located Markdown serializer:
Because it serializes structured data rather than rendering components, a block can never leak as a raw <Component/> tag. An unknown block type simply returns an empty string and is filtered out. That is also why adding a new page-builder block includes adding its Markdown serializer — without one, the block renders blank in .md output.
Headers that keep caches honest
The Markdown response is careful about caching and crawlers. It sets Vary: Accept so a shared cache never serves Markdown to a browser, content-location pointing at the canonical HTML page, and x-robots-tag: noindex, nofollow to keep the Markdown twin out of search results. A short s-maxage with stale-while-revalidate bounds CDN drift, since the rendered response itself is not tag-purged.
There is even graceful failure: an upstream fetch error returns a 503, not a 404, so crawlers do not treat a transient blip as a page that is gone. Redirects modelled in Sanity are honoured too, mapped to their .md form and restricted to same-origin destinations.
Why bother
It is a small amount of infrastructure for an outsized payoff. Your content becomes cheap for agents to read, your structured data stays the single source of truth, and you never maintain a parallel Markdown copy that drifts out of date.