Skip to content
SeenLens

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.

  1. Content negotiation. SeenLens requests the page with Accept: text/markdown. The route works when the answer is 200 with a text/markdown content type.
  2. 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.
  3. A .md sibling. SeenLens requests the page path with .md added, such as /pricing.md, or /index.md for 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.

Terminal
$ 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: 953

content-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:

Body (first lines)
---
title: "Pricing: AI Visibility Report, USD 49 · SeenLens"
url: https://seenlens.com/pricing
last_updated: 2026-11-05
---

# Pricing

Do 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.

middleware.ts
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.

nginx.conf
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".

HTML head
<link rel="alternate" type="text/markdown" href="/pricing.md">

How do I verify the fix?

Terminal
curl -sI -H 'Accept: text/markdown' https://example.com/page
curl -sI https://example.com/page
  1. The first answer must show 200, a text/markdown content type and Vary: Accept.
  2. The second must still show text/html.
  3. Scan the page with SeenLens. The evidence line lists the status, content type and Vary result.

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.

Scan your page with SeenLens

Sources