The Accept: text/markdown convention
What agent clients send, what your server should match on, and the exact media-type string defined by RFC 7763.
The media type registered with IANA for Markdown is text/markdown,
defined in RFC 7763. That’s
the string clients should send in Accept, and the string your server
should send in Content-Type.
What agent clients actually send
Most well-behaved agent clients send something like one of these:
Accept: text/markdown
Accept: text/markdown, text/html;q=0.8
Accept: text/markdown, text/plain;q=0.5, */*;q=0.1
The first preference is text/markdown. If the server can’t do that,
the client falls back to HTML or plain text. The q=0.8 means “accept
HTML at 80% of the preference given to Markdown.”
Some clients are lazier and send Accept: text/plain or */*. Those
aren’t asking for Markdown specifically, so your server should serve
HTML (or whatever your default is).
What your server should match on
Don’t substring-match on text/markdown. A naive accept.includes("text/markdown")
happens to work, but
accept.startsWith("text/html") breaks on real Chrome headers. Use a
proper Accept parser — see
Accept parsing & q-values for what to watch
for.
The short version: parse the header into an ordered list of type + q-value, sort by q, break ties by specificity (more specific wins over wildcards), and pick the highest-ranked type you can produce.
Content-Type on the response
If you served Markdown, say so:
Content-Type: text/markdown; charset=utf-8
The charset=utf-8 parameter is a good idea — Markdown is just text,
but explicit is safer.
Optional: the variant parameter
RFC 7764 defines a variant parameter for distinguishing Markdown
flavors:
Content-Type: text/markdown; charset=utf-8; variant=CommonMark
Content-Type: text/markdown; charset=utf-8; variant=GFM
The variant parameter is uncommon in public traffic, and most clients don’t request it. You don’t need it. But if you’re being thorough about which flavor of Markdown you serve, it’s there.
What not to use
text/x-markdown— deprecated, predates the registrationapplication/markdown— not registered, not conventional
Related but not the same
.mdin the URL path (/article.mdas a sibling of/article) is fine to ship, but agents have no way to discover it. They hit the canonical URL —/article— and read whatever it returns. If that’s HTML, HTML is what ends up in their context./llms.txtis sometimes proposed as a way to advertise sibling URLs to agents, but adoption in the wild is thin — don’t count on it.- Content negotiation via
Accept: text/markdownon the canonical URL doesn’t require discovery. The agent hits the URL it already knows. Your server picks the right bytes. Every HTTP client speaks Accept — that’s the difference. - Ship
.mdsiblings if you want; they don’t hurt. Just don’t rely on them as the only path.
Advertising .md siblings via Link / rel="alternate"
If you do ship siblings and want to make them discoverable, advertise
them per RFC 8288 — either as
an HTTP Link header or an HTML <link> element:
Link: </article.md>; rel="alternate"; type="text/markdown"
<link rel="alternate" type="text/markdown" href="/article.md">
Both declare “the same resource is also available at this URL in this
format.” Agents and search engines that parse rel="alternate" can
follow it. Adoption in real-world agents is thin today, but the
markup is spec-compliant and costs nothing.
This is complementary to content negotiation, not a substitute. If you
have both, hitting /article with Accept: text/markdown still works
for any agent; rel="alternate" just gives the ones that look for it
a second path.