# No markdown content negotiation

> Serving a markdown version to agents that ask for it lets them read your content without parsing the DOM. Add Accept: text/markdown negotiation.

_Category: AI readiness · Detector `aeo` · Check `markdown-negotiation-missing` · Severity: info_

When an agent requests your page with `Accept: text/markdown` and gets HTML back, it has to parse the full DOM — navigation, scripts, styling — to recover the content. Offering a markdown representation of the same URL hands agents clean, structured text directly. It’s an advanced, optional signal, but a strong one for agent-first sites and docs.

## Symptoms

- Requesting the page with `Accept: text/markdown` returns HTML
- Agents parse noisy DOM instead of clean content
- No lightweight text representation for programmatic consumers

## How VibeCheck detects it

The `aeo` detector flags this live in the browser and reports it to the widget's Problems list — and to your coding agent over MCP.

- **Issue string:** `No markdown content negotiation`
- **Threshold:** A request with Accept: text/markdown still returns an HTML content type

## Root causes

- The server ignores the `Accept` header and always returns HTML
- No markdown source or renderer exposed for content routes

## The fix

Honour the `Accept` header: when a request prefers `text/markdown`, respond with a markdown representation of the same content and a `text/markdown` content type. For content built from markdown already, this is often just serving the source. Add a `Vary: Accept` header so caches keep the two representations separate.

### Steps

1. Detect `Accept: text/markdown` on your content routes
2. Return the markdown representation with a `text/markdown` content type
3. Send `Vary: Accept` so caches don’t mix HTML and markdown responses

_Negotiate on the Accept header_

```js
if (req.headers.accept?.includes('text/markdown')) {
  res.setHeader('Content-Type', 'text/markdown; charset=utf-8')
  res.setHeader('Vary', 'Accept')
  return res.end(page.markdownSource)
}
```

## Framework-specific fixes

### Next.js

Branch on the `Accept` header inside a Route Handler (or Middleware) and return the markdown source with the right content type and `Vary` header.

_app/blog/[slug]/route.ts (or middleware)_

```ts
export async function GET(req: Request, { params }) {
  const post = await getPost((await params).slug)
  if (req.headers.get('accept')?.includes('text/markdown')) {
    return new Response(post.markdown, {
      headers: { 'Content-Type': 'text/markdown; charset=utf-8', Vary: 'Accept' },
    })
  }
  return new Response(renderHtml(post), { headers: { 'Content-Type': 'text/html' } })
}
```

### Vanilla JS

In any server, check the `Accept` header on content routes and serve the markdown source when it’s preferred.

```js
app.get('/docs/:slug', (req, res) => {
  const wantsMd = (req.get('Accept') || '').includes('text/markdown')
  res.vary('Accept')
  res.type(wantsMd ? 'text/markdown' : 'text/html').send(wantsMd ? doc.md : doc.html)
})
```

## FAQ

### Is markdown negotiation required?

No — it’s an optional, advanced AEO signal, most valuable for docs and agent-facing sites. VibeCheck reports it as info, not an error. Skip it if your content isn’t markdown-backed.

### Why send `Vary: Accept`?

So shared caches and CDNs store the HTML and markdown responses separately per `Accept` header, instead of serving one to a client that asked for the other.

### How is this different from `llms.txt`?

`llms.txt` is one static index of your key content for LLMs; markdown negotiation serves a clean markdown version of any URL on request. They complement each other — `llms.txt` points agents at the content, negotiation delivers it without the DOM noise.

## Related problems

- [Missing llms.txt](https://vibecheck.wcgw.fun/fix/missing-llms-txt.md) — AI readiness
- [Content only renders with JavaScript](https://vibecheck.wcgw.fun/fix/content-requires-javascript.md) — AI readiness
- [Missing structured data (JSON-LD)](https://vibecheck.wcgw.fun/fix/missing-structured-data.md) — AI readiness
- [No agent interface (MCP) advertised](https://vibecheck.wcgw.fun/fix/missing-mcp-discovery.md) — AI readiness

---

Fix guide from VibeCheck — https://vibecheck.wcgw.fun/fix/missing-markdown-negotiation. Full site index for LLMs: https://vibecheck.wcgw.fun/llms.txt
