HTTP 406 Not Acceptable

HTTP 406 Not Acceptable means the server cannot produce a response that matches any of the formats the client declared acceptable in its Accept request headers. The resource exists and is accessible — this is not a 404 — but the server has no representation of it that satisfies the client’s requirements. The client must relax its Accept constraints, check the 406 body for what formats are available, or accept a different format.

HTTP 406 quick reference →

Quick reference

Code406
NameNot Acceptable
Category4xx Client Error
SpecificationRFC 9110 §15.5.7
Triggered byAccept, Accept-Language, Accept-Encoding, Accept-Charset headers
Cacheable?No

Content negotiation and Accept headers

HTTP content negotiation lets clients declare what formats they can process. The server selects the best available representation. The relevant request headers:

Accept — acceptable response MIME types. Accept: application/json means only JSON. Accept: text/html, */*;q=0.9 means HTML preferred but anything acceptable as fallback.

Accept-Language — acceptable response languages. Accept-Language: de, en;q=0.8 means German preferred, English as fallback.

Accept-Encoding — acceptable content compression formats. Accept-Encoding: gzip, br.

Accept-Charset — acceptable character encodings. Rarely used since UTF-8 is universal.

406 is returned when no combination of available representations satisfies all of the client’s Accept headers. A client sending Accept: application/json to an XML-only API receives 406 because the server has no JSON representation to offer.

Common causes and examples

JSON client hitting XML-only API:

GET /api/products/42 HTTP/1.1
Accept: application/json

HTTP/1.1 406 Not Acceptable
Content-Type: application/json

{"error": "not_acceptable",
 "available": ["application/xml", "text/xml"],
 "message": "This endpoint only produces XML"}

API versioning via Accept header: Some APIs encode versions in the media type. A client requesting Accept: application/vnd.example+json; version=4 receives 406 if the server only supports versions 1–3.

GET /api/orders/7829 HTTP/1.1
Accept: application/vnd.example+json; version=4

HTTP/1.1 406 Not Acceptable
{"supported_versions": [1, 2, 3]}

Language negotiation failure: A localized API returns 406 when the client requests a language it does not have content for.

GET /articles/hello-world HTTP/1.1
Accept-Language: ja, zh;q=0.9

HTTP/1.1 406 Not Acceptable
{"available_languages": ["en", "fr", "de", "es"]}

Framework content negotiation: Rails, Django REST Framework, ASP.NET Core, and Spring all perform content negotiation automatically. If no renderer supports the requested content type, they return 406 without any application-level code needed.

Fixing 406 as a client

First, read the response body. A well-implemented 406 body lists the available media types, languages, or versions. Use one of those in subsequent requests.

Second, check whether the Accept header is too restrictive. Including */* as a fallback allows the server to respond with any available format:

# Too restrictive (triggers 406 on XML-only endpoints):
curl -H "Accept: application/json" https://api.example.com/data

# More lenient (fallback to any format):
curl -H "Accept: application/json, */*;q=0.8" https://api.example.com/data

Third, for API versioning via Accept: check the API changelog for currently supported versions and update the Accept header accordingly.

Server-side implementation

Express.js:

app.get('/api/products/:id', (req, res) => {
  const product = getProduct(req.params.id);
  if (req.accepts('application/json')) {
    return res.json(product);
  }
  if (req.accepts('application/xml')) {
    return res.type('xml').send(toXml(product));
  }
  return res.status(406).json({
    error: 'not_acceptable',
    available: ['application/json', 'application/xml']
  });
});

Django REST Framework: DRF uses renderer classes. If the client requests a format with no registered renderer, DRF returns 406 automatically. Register renderers globally or per-view:

from rest_framework.renderers import JSONRenderer, XMLRenderer

class ProductView(RetrieveAPIView):
    renderer_classes = [JSONRenderer, XMLRenderer]
    serializer_class = ProductSerializer
    # DRF returns 406 automatically for other Accept types

nginx proactive content negotiation: nginx can serve different files based on Accept headers using the try_files directive with format-suffixed files, though most format negotiation is better handled at the application layer.

406 vs 415 vs 300

CodeDirectionMeaning
406Response formatServer cannot produce a response in the client’s accepted format
415Request body formatServer cannot process the format the client sent in the request body
300Multiple optionsServer offers a list of available representations for the client to choose from

Frequently asked questions

What does HTTP 406 mean?

The resource exists but the server cannot respond in any format the client declared acceptable in its Accept headers. Relax the Accept constraint (add a */* fallback) or check the 406 response body for what formats the server supports.

What is the difference between 406 and 415?

406 is about the response format (what the server sends to the client). 415 is about the request body format (what the client sent to the server). They are opposite directions in content negotiation.

Can I avoid 406 by using Accept: */*?

Yes. Accept: */* tells the server the client accepts any response format. The server serves its default format and will not return 406. Use this when you have no format preference and just want the server to respond.

Is 406 always a client error?

In most cases yes — the client specified Accept headers the server cannot satisfy. However, if a server is missing a renderer for a format it should support (e.g., JSON missing from a JSON API), the 406 represents a server misconfiguration even though it uses a 4xx code.

Related guides

HTTP 415 Unsupported Media Type · HTTP 400 Bad Request

Comparisons

HTTP 406 vs 415

Standards reference

Definitions from the IANA HTTP Status Code Registry and RFC 9110 §15.5.7. Human-readable guidance by ErrorLookup. · HTTP 406 quick reference →