Markdown for agents: how to serve Accept: text/markdown
Markdown for agents means the same URL returns Markdown when a client sends Accept: text/markdown, and HTML for browsers. SeenLens reports info when no Markdown version exists. The feature is optional, and no major agent is confirmed to ask for it. Setup takes one to three hours, depending on the stack.
By SeenLens · Last updated
Run the free 43-check scan on your page
What does the Markdown version check test?
SeenLens tries three routes to a Markdown version of the scanned page. One working route is enough.
- Content negotiation. SeenLens requests the page with
Accept: text/markdown. The route works when the answer is 200 with atext/markdowncontent type. - A link alternate. The page head holds
<link rel="alternate" type="text/markdown" href="...">. SeenLens fetches up to three such links. The route works when one returns 200 and is not HTML. - A
.mdsibling. SeenLens requests the page path with.mdadded, such as/pricing.md, or/index.mdfor the home page. The route works when the answer is 200 and not HTML.
The result is a pass when a route works. If the page uses content negotiation without Vary: Accept, the result is a warning, because a shared cache may then serve Markdown to browsers or HTML to agents. When no route works, the result is info. That is the lowest severity, because the feature is optional.
What does a working response look like?
These are the real response headers of https://seenlens.com/pricing for a request with Accept: text/markdown, taken on 2026-11-06. The list is trimmed.
$ curl -sI -H 'Accept: text/markdown' https://seenlens.com/pricing
HTTP/2 200
cache-control: s-maxage=31536000
content-type: text/markdown; charset=utf-8
link: <https://seenlens.com/pricing>; rel="canonical"
vary: Accept
x-content-type-options: nosniff
x-markdown-tokens: 953content-type shows Markdown. vary: Accept tells caches that the answer depends on the Accept header. link ... rel="canonical" points search engines to the HTML URL, so the Markdown copy does not compete with it. x-markdown-tokens is an estimated token count for the document. The same URL without the Accept header returns content-type: text/html; charset=utf-8.
The body starts with a short front matter block (title, description, url, last updated), then the page as Markdown:
---
title: "Pricing: AI Visibility Report, USD 49 · SeenLens"
url: https://seenlens.com/pricing
last_updated: 2026-11-05
---
# PricingDo agents ask for Markdown?
The evidence is early. Cloudflare announced a Markdown for Agents feature on 2026-02-12. It converts HTML to Markdown at the edge when a client sends Accept: text/markdown. Google says Google Search does not use llms.txt files, AI text files, special markup or Markdown. SeenLens found no source that confirms which consumer AI agents request Markdown. Serve it for tools and developers who send the header, and expect no ranking or citation effect.
Content negotiation is part of HTTP: RFC 9110 defines the Accept request field and the Vary response field. See Markdown content negotiation.
How do I serve Markdown to agents?
Keep the Markdown equivalent to the HTML. Send Vary: Accept on both the Markdown and the HTML answers of a negotiated URL. Add a canonical link header to the Markdown answer.
Next.js App Router
SeenLens uses a middleware that rewrites a request to an internal Markdown route handler when the client prefers Markdown or the path ends in .md. This is a reduced version of it.
import { NextResponse, type NextRequest } from "next/server";
export function middleware(req: NextRequest) {
const { pathname } = req.nextUrl;
const accept = req.headers.get("accept") ?? "";
const wantsMd = pathname.endsWith(".md") || accept.includes("text/markdown");
if (!wantsMd) return NextResponse.next();
const base = pathname.replace(/\.md$/, "") || "/";
const url = req.nextUrl.clone();
url.pathname = "/md" + (base === "/" ? "/index" : base);
const res = NextResponse.rewrite(url);
res.headers.set("Vary", "Accept");
return res;
}
export const config = { matcher: ["/", "/pricing", "/(.+\\.md)"] };A route handler at app/md/[...path]/route.ts returns the Markdown with Content-Type: text/markdown; charset=utf-8. Parse the Accept header properly in production code. A header such as text/html, text/markdown;q=0.1 prefers HTML, and the simple includes check above would send Markdown. SeenLens compares the q values.
One gotcha from the SeenLens code: Next.js sets its own Vary header on prerendered HTML and drops one set in middleware. Add Vary: Accept for the HTML answers at the proxy or CDN.
Nginx with Markdown files next to the HTML
Build a .md file next to each page. When the client asks for Markdown, Nginx rewrites the request to the .md file. This is an example to adapt. Test it with nginx -t and curl before you reload the server.
map $http_accept $want_md {
default 0;
"~*text/markdown" 1;
}
server {
# ...
location / {
add_header Vary Accept always;
if ($want_md) {
rewrite ^/$ /index.md last;
rewrite ^(/.+?)/?$ $1.md last;
}
try_files $uri $uri/index.html =404;
}
location ~ \.md$ {
default_type text/markdown;
charset utf-8;
add_header Vary Accept always;
}
}Cloudflare
Cloudflare's Markdown for Agents converts pages at the edge. Turn it on for the zone from the dashboard Quick Actions. The announcement listed the beta as free for Pro, Business and Enterprise plans, so check current availability.
WordPress, Shopify and hosted platforms
SeenLens found no first party documentation for a WordPress or Shopify setting that serves Markdown by content negotiation. Two options remain: put Cloudflare in front of the site, or skip this check because it is optional.
Static sites
Write the Markdown copies at build time, one .md file per page, and add a link alternate to each HTML page. The llmstxt.org proposal also describes rel="alternate" with type="text/markdown".
<link rel="alternate" type="text/markdown" href="/pricing.md">How do I verify the fix?
curl -sI -H 'Accept: text/markdown' https://example.com/page
curl -sI https://example.com/page- The first answer must show 200, a
text/markdowncontent type andVary: Accept. - The second must still show
text/html. - Scan the page with SeenLens. The evidence line lists the status, content type and
Varyresult.
When can I skip this?
Skip it if you have no need for Markdown copies. The result is info, which earns half credit on a check with weight 3 in the agent readiness dimension. A site that shows nothing without JavaScript should fix server rendering first, because that affects every crawler.
Sources
- Cloudflare: Markdown for Agents (Accept: text/markdown conversion, response headers, plan availability; published 2026-02-12; checked 2026-11-06)
- RFC 9110: HTTP Semantics (Accept in section 12.5.1 and Vary in section 12.5.5; checked 2026-11-06)
- Google Search Central: Optimizing your website for generative AI features (Google Search does not use Markdown or AI text files; checked 2026-11-06)
- llms.txt proposal (.md page versions and rel="alternate" type="text/markdown"; checked 2026-11-06)
- Next.js docs: route.js (route handlers; checked 2026-11-06)