# Returning 406 Not Acceptable

When an `Accept` header genuinely can't be satisfied, `406` is the right response. The common mistake is returning it too eagerly.

`406 Not Acceptable` is the HTTP response code for *I understood your
request, but I cannot produce a response in any of the formats you'll
accept.* It's defined in
[RFC 9110 §15.5.7](https://www.rfc-editor.org/rfc/rfc9110#name-406-not-acceptable).

## When to return 406

Return 406 when **every** representation you can produce has either:
- no match in the client's Accept list, *or*
- a match with `q=0` (explicitly rejected)

Concrete scenarios:
- Client sends `Accept: application/pdf`, your server can only do
  HTML and Markdown → 406.
- Client sends `Accept: text/markdown;q=0`, your server only has
  Markdown → 406.

## When **not** to return 406

Most implementation bugs happen here: returning 406 too aggressively. Do not 406 when:

- **The Accept header is missing.** That means "no constraint," not
  "nothing works." Serve your default.
- **Accept is `*/*`.** Same as missing — serve your default.
- **One of your representations didn't match, but another did.** Pick
  the one that matches.
- **You're not sure.** A spec-correct default (usually HTML) is better
  than 406 for user experience.

The rule: 406 means *I tried all my options and none work for you*. It
should be rare.

## What to put in the response body

RFC 9110 recommends that a 406 response **should** include a body that
lists the available representations, so the client can pick one and
retry:

```http
HTTP/1.1 406 Not Acceptable
Content-Type: text/plain; charset=utf-8

This resource is available in:
- text/html
- text/markdown

You requested: application/pdf
```

A human-readable plain-text body is the easiest. Some implementations
use the `Link` header with `rel="alternate"` entries, but plain text is
more broadly useful.

## Status class, caching, and retries

- **406 is a client error** (4xx). That signals the client sent a
  request the server can't satisfy.
- **Don't cache 406 aggressively.** The Accept header is request-specific;
  the same URL may return 200 for a different client. `Vary: Accept`
  applies here too, or just `Cache-Control: no-store` for 406 responses.
- **Agents should not retry without changing Accept.** If an agent sees
  a 406, it knows the next request needs a different Accept value.

## Should you fall back silently instead?

There's a pragmatic argument for always returning a representation
rather than 406, even if it doesn't match Accept. Some sites do this
for resilience — better to serve HTML than to error.

The tradeoff: silent fallback masks a real mismatch. If an agent asked
for Markdown and got HTML with `Content-Type: text/html`, the agent may
treat the response as wrong format and either ignore it or mis-parse
it. A clean 406 is more honest.

The site's recommendation: return 406 when you genuinely can't satisfy
the request, and keep your default narrow enough that 406 is rare.