Skip to content
Control Plane Labs

506 Variant Also Negotiates

Server error response defined by RFC 2295 §8.1.

Last updated July 27, 2026

Common causes at a glance

  • Apache MultiViews or a type-map pointing at another negotiable resource
  • A hand-written type-map file listing a variant that is itself a variant list
  • A server implementing RFC 2295 with a cyclic variant configuration
  • A framework reusing 506 for an unrelated internal error, which is simply wrong

Reproduce it

curl -sSI -H 'Negotiate: 1.0' https://www.example.com/doc

What 506 Variant Also Negotiates means

Transparent content negotiation was an attempt to let a client pick among representations using a machine-readable list of variants, rather than having the server guess from Accept headers. A negotiable resource points at a set of concrete variants — a French HTML page, an English one, a PDF — and each variant is supposed to be a leaf.

506 fires when a variant turns out to be another negotiable resource, which would start the negotiation again with no guarantee of terminating. The server detects the cycle and refuses. In practice this means someone enabled Apache MultiViews or a type-map and pointed it at a target that is itself negotiated. This is a museum piece: the modern web negotiates with Accept, Accept-Language, and Vary, and you can spend a career without meeting a 506.

What the spec says

506 is defined in RFC 2295 §8.1, “Transparent Content Negotiation in HTTP”, which is Experimental and has never advanced. The definition is short: the server has an internal configuration error because the chosen variant resource is configured to engage in transparent content negotiation itself and is therefore not a proper endpoint. The surrounding document defines the Negotiate, Alternates, and TCN header fields the scheme depends on. The IANA registry still lists 506 against RFC 2295, which is why it keeps appearing in status tables alongside codes you actually use.

What actually causes it

Apache mod_negotiation is realistically the only implementation you might trip over. Enabling Options +MultiViews on a directory and then adding a .var type-map whose entries resolve back into the same negotiated space produces exactly the loop the code exists to report. The other way to see 506 is somebody picking it out of a status list because it sounded vaguely applicable. A service returning 506 for a validation failure or a routing error should return 500 or an appropriate 4xx instead.

How to debug it

Confirm you are actually in a negotiation flow before investigating anything else. A transparently negotiated response carries a TCN header and an Alternates list:

curl -sSI -H 'Negotiate: 1.0' https://www.example.com/doc | grep -Ei 'HTTP|tcn|alternates|vary'

If neither header is present, whatever produced the 506 is not doing RFC 2295 negotiation and the code is coming from application logic — go read that code. If the headers are there, the fix is in server configuration: find the variant named in the Alternates list and check whether it resolves to another negotiable resource. Disabling MultiViews and serving explicit paths removes the whole class of problem, at no cost that Accept-based negotiation cannot cover.

Working examples

curl

# Ask for transparent negotiation explicitly and inspect the headers.
curl -sSI -H 'Negotiate: 1.0' https://www.example.com/doc \
  | grep -Ei 'HTTP|tcn|alternates|vary|content-location'

# Compare with ordinary Accept-based negotiation, which is what you should use.
curl -sSI -H 'Accept-Language: fr' https://www.example.com/doc | grep -Ei 'HTTP|vary'

Python (requests)

import requests

resp = requests.get(
    "https://www.example.com/doc",
    headers={"Negotiate": "1.0"},
    timeout=10,
)
print(resp.status_code)
print("TCN:", resp.headers.get("TCN"))
print("Alternates:", resp.headers.get("Alternates"))

if resp.status_code == 506:
    # The named variant is itself negotiable: a server-side config loop.
    print("nothing a client can fix here")

Node (fetch)

const res = await fetch("https://www.example.com/doc", {
  headers: { Negotiate: "1.0" },
});

console.log(res.status);
console.log("tcn:", res.headers.get("tcn"));
console.log("alternates:", res.headers.get("alternates"));

if (res.status === 506) {
  // The variant list points at another negotiable resource. Server-side fix only.
  console.error("RFC 2295 negotiation loop on the server");
}

Reference and tooling

The authoritative list of assigned status codes is theIANA HTTP Status Code Registry. For a searchable copy with the semantics summarised, use theHTTP status code reference on this site. To see exactly what a server is sending, theheader inspector shows the raw status line and response headers, and thecurl builder assembles the request flags for you.

Other 5xx codes