Skip to content

HTTP status code · Client errors (4xx)

406 Not Acceptable

406 Not Acceptable means the server cannot produce a response in any format, language or encoding that your Accept headers allow, and it chose not to send a default. It usually comes from an API client sending a strict Accept header, like Accept: application/xml to a JSON-only API.

Facts about this status code
Class4xx, Client errors
Defined inRFC 9110 §15.5.7
Cacheable by defaultOnly with explicit Cache-Control or Expires
Safe to retryYes, with broader Accept, Accept-Language or Accept-Encoding headers
Relevant headers
  • Accept: the request header most often behind a 406; also Accept-Language and Accept-Encoding
  • Vary: on negotiated responses, tells caches which request headers changed the answer

What 406 means

RFC 9110, section 15.5.7, ties 406 to proactive negotiation: the client lists what it can handle in Accept, Accept-Language and Accept-Encoding, and the server has no matching representation and is unwilling to supply a default. The server should list the available alternatives in the response body.

Most servers never send it for browsers. Browsers send Accept values ending in */*, and servers generally ignore unmatched preferences and return their default format, which the spec explicitly permits. The code shows up with strict API frameworks: Spring MVC, for example, throws HttpMediaTypeNotAcceptableException and answers 406 when no converter can produce the requested type. Some shared hosts also configure ModSecurity to answer blocked requests with 406, which has nothing to do with negotiation.

Common causes

If you are visiting the site

  • A security module on the host (often ModSecurity) flagged something in the request, such as a form field or query string, and answers with 406.

If you run the server

  • The client sends Accept: application/xml, text/csv or a vendor type the endpoint cannot produce.
  • An endpoint that returns a file download is called with Accept: application/json by a generic HTTP client.
  • Strict Accept-Language or Accept-Encoding with no wildcard, for example Accept-Encoding: br, identity;q=0 on a server without Brotli.

How to fix it

If you are visiting the site

  • Remove unusual characters or code snippets from what you typed into the form and try again, then tell the site owner what triggered it.

If you run the server

  • Send an Accept header the endpoint documents, or add */* with a lower q-value as a fallback (Accept: application/json, */*;q=0.8).
  • On the server, prefer returning your default representation over 406 unless a wrong format would break the client.
  • If ModSecurity is the source, find the rule ID in the audit log and tune or whitelist it for that path.

How to send 406

Express (Node.js)
app.get('/reports/:id/export', (req, res) => {
  res.status(406).json({ error: 'Not acceptable', available: ['application/json', 'text/csv'] });
});
Next.js App Router route handler
// app/reports/[id]/export/route.ts
export async function GET() {
  return Response.json(
    { error: 'Not acceptable', available: ['application/json', 'text/csv'] },
    { status: 406 }
  );
}
Go net/http
mux.HandleFunc("GET /reports/{id}/export", func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusNotAcceptable) // 406
	w.Write([]byte(`{"error":"Not acceptable","available":["application/json","text/csv"]}`))
})
Python FastAPI
from fastapi import FastAPI
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get("/reports/{id}/export")
def export_report(id: str):
    return JSONResponse(status_code=406, content={"error": "Not acceptable", "available": ["application/json", "text/csv"]})

Commonly confused with

406 vs 415
415 is about the format of what the client sent (Content-Type); 406 is about the format the client wants back (Accept).
406 vs 300
300 Multiple Choices offers several representations to pick from; 406 says none of them fit what was asked for.

Frequently asked questions

How do I fix a 406 Not Acceptable from an API?
Check the Accept header your client sends. Set it to a type the API documents, usually application/json, or add */*;q=0.8 so the server can fall back to its default.
Why do I get 406 on a WordPress or shared hosting site?
Many shared hosts run ModSecurity with 406 as the response for blocked requests. It is a firewall match, not content negotiation, and the host can tell you which rule fired.
Is 406 the same as 415?
No. 415 Unsupported Media Type rejects the format of the request body, set by Content-Type. 406 rejects the formats the client asked to receive, set by Accept.

Last reviewed by Arielton Oberek.