> ## Documentation Index
> Fetch the complete documentation index at: https://docs.37audits.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Markdown Content Auditor

> Validates that the site serves HTML to browsers and markdown to AI clients via Accept content negotiation

<Info>
  **AI Readiness Auditor** — validates that the site serves HTML to browsers and markdown to AI clients via `Accept` content negotiation.
</Info>

<CardGroup cols={2}>
  <Card title="AI Readiness" icon="bot">
    AI agents that ask for markdown get clean markdown source instead of a full HTML page.
  </Card>

  <Card title="Compatibility" icon="globe">
    Browsers keep receiving HTML by default; markdown is only served when explicitly requested.
  </Card>
</CardGroup>

Large language models and AI agents consume markdown far more efficiently than rendered HTML: it is smaller, keeps the document structure, and drops navigation, scripts, and styling noise. A growing convention is to serve the same URL as markdown when the client sends `Accept: text/markdown`, while browsers — which send `Accept: text/html` — keep receiving the normal HTML page. This auditor requests the audited URL both ways and verifies each response's `Content-Type` and body.

## How it works

The auditor issues two GET requests to the audited URL (following redirects, with HTTP Basic Auth when provided):

1. **Browser request** — sends `Accept: text/html` and expects a `Content-Type` of `text/html` or `application/xhtml+xml`.
2. **AI-client request** — sends `Accept: text/markdown` and expects a `Content-Type` of `text/markdown` or `text/x-markdown`.
3. **Body sanity check** — when the markdown response declares a markdown media type, the body must not start as a full HTML document (`<!DOCTYPE html` or `<html`).
4. **Aggregate** — any failing rule makes the audit `FAIL`; when every rule passes, the audit is `SUCCESS`.

Media type parameters such as `;charset=UTF-8` are ignored when comparing content types. An unreachable URL produces an `ERROR` response; a non-200 status on either request produces a `FAIL` without evaluating the rules.

## What it audits

Every row maps to one `checkRule*` method and one `CheckCode`.

| CheckCode                                                                 | Status    | When it fires                                                                                                                                                                                  | Recommendation                                                                                                                                       |
| ------------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| <a id="37A-MarkdownContentAuditor-400" />`37A-MarkdownContentAuditor-400` | `FAIL`    | The request with `Accept: text/html` returns a missing or blank `Content-Type` header.                                                                                                         | Send `Content-Type: text/html` for `{url}` when the client asks for HTML.                                                                            |
| <a id="37A-MarkdownContentAuditor-401" />`37A-MarkdownContentAuditor-401` | `FAIL`    | The request with `Accept: text/html` is answered with a markdown media type (`text/markdown` or `text/x-markdown`).                                                                            | Serve HTML to browsers: set Content-Type to `text/html` on `{url}` for `Accept: text/html` instead of `{contentType}`.                               |
| <a id="37A-MarkdownContentAuditor-402" />`37A-MarkdownContentAuditor-402` | `FAIL`    | The request with `Accept: text/html` is answered with a Content-Type that is present, not markdown, and not `text/html` or `application/xhtml+xml`.                                            | Serve `{url}` as `text/html` when Accept is `text/html`; current Content-Type is `{contentType}`.                                                    |
| <a id="37A-MarkdownContentAuditor-403" />`37A-MarkdownContentAuditor-403` | `FAIL`    | The request with `Accept: text/markdown` returns a missing or blank `Content-Type` header.                                                                                                     | Send `Content-Type: text/markdown` for `{url}` when the client sends `Accept: text/markdown`.                                                        |
| <a id="37A-MarkdownContentAuditor-404" />`37A-MarkdownContentAuditor-404` | `FAIL`    | The request with `Accept: text/markdown` is still answered with an HTML media type (`text/html` or `application/xhtml+xml`), meaning the server does not negotiate markdown.                   | Negotiate markdown: serve `{url}` as `text/markdown` (or `text/x-markdown`) when Accept is `text/markdown`; current Content-Type is `{contentType}`. |
| <a id="37A-MarkdownContentAuditor-405" />`37A-MarkdownContentAuditor-405` | `FAIL`    | The request with `Accept: text/markdown` is answered with a Content-Type that is present, not HTML, and not `text/markdown` or `text/x-markdown`.                                              | Serve `{url}` as `text/markdown` when Accept is `text/markdown`; current Content-Type is `{contentType}`.                                            |
| <a id="37A-MarkdownContentAuditor-406" />`37A-MarkdownContentAuditor-406` | `FAIL`    | The markdown response declares a markdown media type but the body starts as a full HTML document (`<!DOCTYPE html` or `<html`).                                                                | Return markdown source at `{url}` for `Accept: text/markdown`; the response labeled `{contentType}` currently begins with HTML.                      |
| <a id="37A-MarkdownContentAuditor-200" />`37A-MarkdownContentAuditor-200` | `SUCCESS` | Every rule above passed; one `SUCCESS` check is added with the message `"{url}" serves HTML ({htmlContentType}) by default and markdown ({markdownContentType}) when Accept is text/markdown.` | —                                                                                                                                                    |

## Output documentation

| Status      | Description                                                             | Test logic                                                                                                                                                                                                                                                                                                                                                                  |
| ----------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **SUCCESS** | The URL serves HTML by default and markdown through content negotiation | `Accept: text/html` returns `text/html` or `application/xhtml+xml`; `Accept: text/markdown` returns `text/markdown` or `text/x-markdown` with a body that does not start as an HTML document. Both requests return HTTP 200.                                                                                                                                                |
| **FAIL**    | Content negotiation is missing, inverted, or mislabeled                 | Missing `Content-Type` on either request (`400`, `403`); markdown served to browsers (`401`); non-HTML default (`402`); HTML still served when markdown was requested (`404`); a non-markdown type on the markdown request (`405`); a markdown-labeled response whose body is HTML (`406`); or a non-200 status on either request. No rule in this auditor emits `WARNING`. |
| **ERROR**   | The URL could not be fetched                                            | The host is unreachable or the request fails before a response is received.                                                                                                                                                                                                                                                                                                 |

## Risks and considerations

### AI readiness

* Agents and LLM crawlers that request `text/markdown` and receive a full HTML page must parse markup, scripts, and navigation chrome, wasting context window and degrading answer quality.
* Serving markdown through `Accept` negotiation keeps a single canonical URL per page — no separate `.md` URLs to publish, crawl, or keep in sync.

### Browser compatibility

* Content negotiation must never leak markdown to browsers: a markdown response to `Accept: text/html` renders as plain text and breaks the page for every visitor.
* Responses that vary by `Accept` should include `Vary: Accept` so caches and CDNs do not serve the markdown variant to browsers or vice versa.

### Correct labeling

* A missing `Content-Type` forces clients to sniff the payload, which is unreliable and a known security concern.
* Declaring `text/markdown` while returning an HTML body is worse than not negotiating at all: clients trust the label and feed HTML into markdown pipelines.
