Picture someone researching your company before a sales call. They open a blog post, copy the content, paste it into Claude with a question like “what do these people actually do, and is this the right vendor for my migration?” What the AI sees is not just your article. It is the article plus your header, navigation, footer, cookie banner, related posts widget, and whatever else the page renders.
That is the primary problem a Markdown version of your blog post solves. Someone copying content for their own AI research gets the clean article, nothing else. The title, the prose, the links, and the frontmatter metadata. No navigation, no tracking scripts, no sidebar.
Whether AI search crawlers actually favor sites that do this is unclear. There is no published evidence that serving Markdown improves your visibility in Claude, Perplexity, Google’s AI Overviews, or ChatGPT search. Some crawlers do follow <link rel="alternate" type="text/markdown"> tags. Others ignore them. The only honest claim is that it is cheap to ship, easy to maintain, and aligned with where AI-first content discovery is probably heading.
Part 1: Why this matters if you don’t write code
The real win is for humans using AI
The clearest use case is a prospect or researcher who wants to understand your content through an AI tool. Copying a rendered webpage into Claude or ChatGPT works badly. Copying a clean Markdown file works well. If your blog posts are the kind of content people genuinely want to reference, discuss, or summarize with AI help, giving them a clean copy is a small act of hospitality.
It is the 2026 equivalent of offering a PDF download of a whitepaper. Low effort, no downside.
AI crawlers are a speculative bonus, not a guarantee
Major AI platforms do fetch Markdown files when they find them. Anthropic’s docs, Vercel’s docs, and Stripe’s docs have served Markdown alternates for months. What nobody has proven is whether any of this moves the needle on citations, ranking, or retrieval in LLM-powered search. It might. It might not. The claims floating around LinkedIn about “AI SEO optimization” are mostly marketing.
Treat this as a reasonable bet with a known low cost, not as a proven growth tactic.
You probably already have Markdown
If your website runs on a headless stack, your blog posts are written in Markdown or stored as structured content that converts to Markdown in a few lines of code. The work to expose that as a Markdown URL is small. So the downside of shipping it, even with uncertain payoff, is close to zero.
It is not a replacement for HTML
Your human visitors still get the full website: design, typography, navigation, the lot. The Markdown version is a second representation served only to clients that ask for it. Nothing about your site’s appearance changes.
The three things to ship
- A Markdown URL for every blog post. For example,
/blog/my-post/in the browser and/blog/my-post.mdfor anyone who wants the clean copy. - A
<link rel="alternate" type="text/markdown" href="...">tag in the HTML head, so tools and crawlers that look for it can discover the Markdown URL. - An
llms.txtfile at the site root that lists the important URLs with short descriptions. A curated index for AI tools, not a full dump of every page.
For background on llms.txt itself, we covered it separately in a practical guide for marketing teams.
Part 2: How to wire it up
In Astro with content collections
Astro’s content collections already parse Markdown files into entries with a body property. You serve that body through a dynamic endpoint that gets prerendered at build time.
Create src/pages/blog/[slug].md.ts:
import { getCollection, type CollectionEntry } from "astro:content";
export async function getStaticPaths() {
const posts = await getCollection("blog", ({ data }) => !data.draft);
return posts.map((post) => ({
params: { slug: post.id },
props: { post },
}));
}
type Props = { post: CollectionEntry<"blog"> };
export async function GET({ props }: { props: Props }) {
const { post } = props;
const { title, description, date, author, tags } = post.data;
const frontmatter = [
"---",
`title: ${JSON.stringify(title)}`,
`description: ${JSON.stringify(description)}`,
`date: ${date.toISOString().split("T")[0]}`,
`author: ${JSON.stringify(author)}`,
`tags: [${tags.map((t) => JSON.stringify(t)).join(", ")}]`,
"---",
].join("\n");
const body = `${frontmatter}\n\n# ${title}\n\n> ${description}\n\n${post.body ?? ""}`;
return new Response(body, {
headers: { "Content-Type": "text/markdown; charset=utf-8" },
});
}
With output: "static" in astro.config.mjs, getStaticPaths prerenders one Markdown file per post at build time. No server, no runtime, just a flat file served by your CDN.
Then update your blog post layout to emit the alternate link tag:
---
const markdownHref = new URL(
Astro.url.pathname.replace(/\/$/, "") + ".md",
Astro.site,
).href;
---
<link rel="alternate" type="text/markdown" href={markdownHref} />
That is the full setup for Astro. Fifteen minutes of work.
With a headless CMS like Storyblok
If your content lives in Storyblok, DatoCMS, or Sanity, the principle is the same. Fetch the content and convert it to Markdown on the way out.
Storyblok’s Rich Text fields return JSON. You convert that JSON to Markdown using Storyblok’s rich text renderer configured to output Markdown instead of HTML. A simplified pattern in an Astro project pulling from Storyblok:
// src/pages/blog/[slug].md.ts
import { getStoryblokApi } from "@storyblok/astro";
import { richTextToMarkdown } from "@/lib/richtext-to-markdown";
export async function getStaticPaths() {
const storyblokApi = getStoryblokApi();
const { data } = await storyblokApi.get("cdn/stories", {
starts_with: "blog/",
version: "published",
});
return data.stories.map((story: any) => ({
params: { slug: story.slug },
props: { story },
}));
}
export async function GET({ props }: { props: { story: any } }) {
const { story } = props;
const markdown = richTextToMarkdown(story.content.body);
const body = `# ${story.name}\n\n> ${story.content.description}\n\n${markdown}`;
return new Response(body, {
headers: { "Content-Type": "text/markdown; charset=utf-8" },
});
}
The conversion step is the only real work. Most teams underestimate how messy their own Rich Text schema is. If you embed custom components (callouts, image grids, video embeds) inside Rich Text, decide upfront how each one maps to Markdown. A callout becomes a blockquote. An image grid becomes a list of image links. A video embed becomes a link to the video URL. Simpler than it looks once you commit to a mapping and stop trying to render layout into a format that has none.
For DatoCMS, the equivalent is datocms-structured-text-to-markdown. For Sanity, use @portabletext/to-markdown. Every serious headless CMS has a Markdown serializer or a community one within a week of looking.
Generate llms.txt from the same source
Never hand-maintain llms.txt. Generate it from the same content source your site uses, so new blog posts show up automatically.
In Astro:
// src/pages/llms.txt.ts
import { getCollection } from "astro:content";
export async function GET() {
const posts = await getCollection("blog", ({ data }) => !data.draft);
const sorted = posts.sort(
(a, b) => b.data.date.getTime() - a.data.date.getTime(),
);
const lines = sorted.map(
(p) =>
`- [${p.data.title}](https://example.com/blog/${p.id}/): ${p.data.description}`,
);
const body = `# Example Company
> One-line description of what you do.
## Blog Posts
${lines.join("\n")}
`;
return new Response(body, {
headers: { "Content-Type": "text/plain; charset=utf-8" },
});
}
Publish a new post, run the build, llms.txt updates itself. One less forgotten manual step.
Do not serve Markdown through a per-request function
The whole point is that Markdown is cheap to read. If you serve it through a Next.js route handler or a Lambda that runs on every request, you lose the performance advantage. Generate the Markdown at build time and serve it as a static file from your CDN. That is how you get sub-100ms response times for the AI client, which matters when your page is one of thirty the agent is comparing.
Sanity check the output
Before calling it done, open a few of your generated Markdown URLs in a browser. Check that the content is complete, the frontmatter is valid, and there is no leftover HTML from components you forgot to serialize. An AI agent that hits a Markdown file full of raw <div> tags is worse off than one that hits nothing at all.
Closing
This is a low-cost experiment, not a proven growth lever. Two endpoints, one link tag, and a build config you already have. From there, anyone who wants to paste your content into an AI tool gets a clean version, and any crawler that eventually standardizes on Markdown finds you ready. If the crawler side never materializes, you still have a cleaner archive of your own content.
If your site is not structured to make this trivial, that is usually a sign of a deeper architectural issue. A headless audit is the fastest way to find out what is blocking you. If you would rather not wire it up yourself, our website subscription team handles the implementation and keeps it working as your content grows.