Ctrl + K
HTTP17 min read

Content Negotiation Explained

Understand HTTP content negotiation, including media types, languages, compression, quality values, request headers, response headers, and practical API examples.

Published: 2026-10-05

HTTP content negotiation allows a client and server to agree on the most appropriate representation of a resource. The same resource can often be represented in different formats, languages, or encodings, and the client can communicate which variants it prefers.

For example, an API might be able to return data as JSON or another media type, while a web page might be available in several languages. A client can express its preferences through request headers such as Accept, Accept-Language, and Accept-Encoding.

Content negotiation is an important part of HTTP because it separates the identity of a resource from the particular representation returned to the client. Understanding this distinction makes HTTP APIs, caching, internationalization, and compression easier to design correctly.

What Is Content Negotiation?

Content negotiation is the process by which a client communicates its preferred representation characteristics and a server selects a representation that can satisfy those preferences.

A resource can have multiple representations. For example, a document could be available as HTML, JSON, XML, or another format. The resource itself and its representations are related, but they are not necessarily the same thing.

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

The Accept header tells the server that the client prefers a representation using the application/json media type.

If the server supports that representation, it can return JSON and identify the selected representation with the Content-Type response header.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 42,
  "name": "Example"
}

Representation vs Resource

Content negotiation becomes easier to understand when distinguishing a resource from its representation. A resource is an abstract target identified by a URI. A representation is the data transferred to describe the current state of that resource in a particular format.

For example, /api/products/42 could identify a product resource. One client might request JSON while another client could request a different supported representation.

ConceptExample
Resource/api/products/42
RepresentationJSON representation of the product
Media typeapplication/json
Request preferenceAccept: application/json
Response descriptionContent-Type: application/json

The Accept Header

The Accept request header is one of the most important content negotiation headers. It tells the server which media types the client can understand or prefers to receive.

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

The media type application/json indicates that the client wants a JSON representation.

A client can also list multiple acceptable media types:

Accept: application/json, text/html

The server can then choose an appropriate representation based on the available representations and the client's preferences.

The Wildcard Media Type

The asterisk can be used as a wildcard in the Accept header. It can indicate that the client accepts any media type within a particular category or any media type at all.

Accept: text/*

Accept: */*

text/* means any media type in the text category, while */* is a general wildcard that matches media types broadly.

Quality Values and the q Parameter

Clients can assign relative preference to alternatives using the q parameter, also called a quality value.

Accept: text/html;q=1.0, application/json;q=0.8, */*;q=0.5

In this example, text/html has the highest stated preference, followed by application/json, while the wildcard has a lower preference.

Quality values are preference weights rather than a guarantee that the server must return the highest-weighted representation. The server still has to consider which representations it actually supports and the rules used by its content negotiation implementation.

Understanding q=0

A quality value of zero indicates that the corresponding option is not acceptable to the client.

Accept: application/json, text/html;q=0

This communicates a preference for JSON while explicitly excluding text/html.

The Content-Type Response Header

Content-Type is not the same as Accept. Accept is normally a request header expressing what representations the client can accept. Content-Type describes the media type of the representation contained in the message body.

GET /api/products/42
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 42,
  "name": "Example"
}

For a response containing JSON, Content-Type tells the client how to interpret the response body.

💡 A useful rule is: Accept describes what the client wants to receive, while Content-Type describes the media type of the message body being sent.

Content Negotiation with Request Bodies

Content negotiation is often confused with the format of data sent by the client. When a request contains a body, Content-Type describes that request body.

POST /api/products
Content-Type: application/json

{
  "name": "Keyboard"
}

Here, Content-Type says that the request body is JSON. If the client also wants to tell the server what response format it prefers, it can use Accept separately.

POST /api/products
Content-Type: application/json
Accept: application/json

{
  "name": "Keyboard"
}

The two headers describe different directions: Content-Type describes the body being sent, while Accept describes a preferred representation for the response.

Accept-Language

Content negotiation is not limited to media types. Accept-Language allows a client to communicate preferred natural languages.

Accept-Language: en-US, en;q=0.9, de;q=0.7

This indicates that the client prefers US English, can also use English more generally, and has a lower preference for German.

A multilingual web application can use this information when selecting an appropriate localized representation.

Language Tags

Language preferences use language tags such as en, en-US, fr, de, or ja. A more specific tag can identify a regional or language variant.

Accept-Language: fr-CA, fr;q=0.9, en;q=0.8

The exact language selection process depends on the server and the available translations. A server should not assume that the first listed language is always available.

Accept-Encoding

Accept-Encoding is used for content coding negotiation. It tells the server which content codings the client can decode.

Accept-Encoding: gzip, br

The server can select a supported encoding and indicate the selected coding with the Content-Encoding response header.

HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: br

The representation is still JSON, but its transferred content is encoded using Brotli.

Media Type vs Content Encoding

Media type and content encoding describe different properties of a response. Content-Type identifies the media type of the representation, while Content-Encoding identifies a content coding applied to that representation.

HeaderPurposeExample
Content-TypeIdentifies the media typeapplication/json
Content-EncodingIdentifies applied content codingbr
AcceptStates acceptable media typesapplication/json
Accept-EncodingStates acceptable content codingsgzip, br

Server-Driven Content Negotiation

The most common form of content negotiation is server-driven negotiation. The client sends preference information in request headers, and the server selects a representation.

GET /documents/42
Accept: application/json
Accept-Language: en-US
Accept-Encoding: br, gzip

The server can consider the requested media type, language, and encoding when constructing its response.

HTTP/1.1 200 OK
Content-Type: application/json
Content-Language: en-US
Content-Encoding: br

The response headers communicate which representation characteristics were selected.

The Vary Header

The Vary response header is important when content negotiation is involved. It tells caches which request headers influenced the selected representation.

HTTP/1.1 200 OK
Content-Type: application/json
Vary: Accept

If the server can return different representations depending on Accept, a cache needs to know that Accept is relevant when deciding whether a stored response can be reused for another request.

For language and encoding negotiation, a response might use:

Vary: Accept, Accept-Language, Accept-Encoding
⚠️ Incorrect Vary configuration can cause caches to reuse a representation for a request whose preferences differ from the request that originally generated the cached response.

Why Vary Matters for APIs and CDNs

Modern applications often place caches or CDNs between clients and application servers. If the server chooses different representations based on request headers, those intermediaries need enough information to distinguish the variants.

For example, if one request receives JSON and another receives a different media type based on Accept, treating both responses as interchangeable could produce an incorrect response.

A Content Negotiation Example

GET /api/users/42
Accept: application/json, text/html;q=0.8
Accept-Language: en-US, en;q=0.9
Accept-Encoding: br, gzip

The client communicates three independent preferences: the desired media types, preferred languages, and acceptable content codings.

HTTP/1.1 200 OK
Content-Type: application/json
Content-Language: en-US
Content-Encoding: br
Vary: Accept, Accept-Language, Accept-Encoding

The server selected JSON, US English, and Brotli encoding. Vary communicates which request headers can affect the representation.

What Happens When No Representation Matches?

If a server cannot provide an acceptable representation for a request, it may respond with 406 Not Acceptable when that status is appropriate to the negotiation failure.

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

{
  "error": "No acceptable representation available"
}

A 406 response communicates that the server could not produce a representation acceptable according to the request's stated preferences.

406 Not Acceptable vs 415 Unsupported Media Type

These status codes are often confused because both relate to representation formats, but they address different situations.

StatusTypical meaning
406 Not AcceptableThe server cannot provide a representation that satisfies the client's requested preferences.
415 Unsupported Media TypeThe server does not support the media type or content coding of the request content in the relevant context.

For example, a client might send a request body using an unsupported media type and receive 415. A client requesting only representations that the server cannot provide may receive 406.

Content Negotiation in REST APIs

REST APIs can use content negotiation when an endpoint supports multiple representations. For example, an API might support JSON for browser and application clients while also exposing another representation for a specialized consumer.

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

A different client could request another supported representation:

GET /api/products/42
Accept: application/xml

The same resource can therefore have multiple representations without requiring completely different resource identities.

Content Negotiation and API Design

Not every API needs to support multiple representations. If an API is intentionally JSON-only, it can document application/json as its representation format and keep negotiation simple.

  • Support only the representations that provide a real use case.
  • Document supported media types.
  • Use Content-Type correctly for request and response bodies.
  • Use Accept when clients can choose between representations.
  • Return appropriate status codes for unsupported representations.
  • Consider cache behavior when representations vary.

Content Negotiation and Browsers

Browsers automatically send several headers that can participate in content negotiation. The exact values depend on the browser, user configuration, request context, and supported features.

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br

A browser can therefore communicate that it accepts HTML and other representations, has language preferences, and supports particular content codings.

Content Negotiation and Internationalization

Language negotiation can be useful when building multilingual applications. A server can use Accept-Language as one signal when selecting a default language.

However, applications should not necessarily make language selection depend exclusively on the browser's preference. Users may explicitly choose a language through application settings, and that explicit preference can be more useful for subsequent requests.

💡 Treat Accept-Language as a useful preference signal, not necessarily as the only source of truth for a user's language choice.

Content Negotiation and Compression

Compression is another common form of HTTP negotiation. A client advertises supported content codings through Accept-Encoding, and the server can select one that is supported and appropriate.

Accept-Encoding: br, gzip

If Brotli is selected, the response can contain:

Content-Encoding: br

This does not change the underlying media type. A JSON response remains application/json even when its transferred representation is compressed.

Content Negotiation vs URL-Based Formats

Applications can sometimes select representations through URLs, query parameters, or file extensions instead of HTTP content negotiation.

/products/42.json
/products/42.xml

Another design can use the same resource URI and let Accept determine the requested representation:

GET /products/42
Accept: application/json

Both approaches can be used in real systems. The appropriate design depends on the API architecture, client requirements, routing model, caching strategy, and compatibility constraints.

Proactive vs Reactive Negotiation

HTTP content negotiation is often described in terms of proactive and reactive approaches. In proactive negotiation, the server selects a representation based on request information such as Accept or Accept-Language.

Reactive negotiation allows the server to provide information that helps the client choose another available representation. The client can then make another request for the selected representation.

In everyday web APIs, server-driven or proactive negotiation is the pattern developers encounter most frequently.

Content Negotiation and Caching Problems

Negotiated responses can create multiple variants of the same resource. This can complicate caching because a cache needs to know which request characteristics influence the selected representation.

For example, if an endpoint returns English or German depending on Accept-Language, the cache cannot safely treat the English and German responses as interchangeable.

Vary: Accept-Language

The Vary header provides the information needed for this distinction.

Content Negotiation and ETags

Different representations of the same resource can have different entity tags. A JSON representation and another representation can therefore require different validators.

When caching negotiated responses, developers should consider both representation selection and cache validation. The representation returned for a request must correspond to the relevant request preferences and validator semantics.

Common Content Negotiation Mistakes

Mistake 1: Confusing Accept and Content-Type

Accept describes acceptable response representations. Content-Type describes the media type of the message body. They are related but not interchangeable.

Mistake 2: Ignoring Accept-Encoding

Applications and infrastructure that support compression need to account for the encodings a client can decode and correctly describe the selected encoding with Content-Encoding.

Mistake 3: Forgetting Vary

If a response varies according to request headers and the cache is not informed about those headers, an intermediary can potentially reuse the wrong representation.

Mistake 4: Treating q Values as Absolute Commands

Quality values express preferences. They do not force the server to provide a representation it does not support.

Mistake 5: Supporting Too Many Representations

Adding multiple formats can increase implementation, testing, documentation, and caching complexity. Representation negotiation should solve an actual compatibility or product requirement.

Testing Content Negotiation

Content negotiation can be tested by sending the same resource request with different Accept, Accept-Language, and Accept-Encoding values and comparing the responses.

GET /api/products/42
Accept: application/json
GET /api/products/42
Accept: application/xml
GET /api/products/42
Accept-Language: de
Accept-Encoding: gzip
  • Check the response Content-Type.
  • Check Content-Language when language negotiation is used.
  • Check Content-Encoding when compression is negotiated.
  • Check Vary when the representation depends on request headers.
  • Test unsupported preferences and expected error handling.
  • Test different q values when the server supports multiple alternatives.

A Practical API Example

Suppose an API exposes a document resource at /api/documents/15. The server supports JSON and XML, and the application supports English and German translations.

GET /api/documents/15
Accept: application/json, application/xml;q=0.8
Accept-Language: de, en;q=0.8
Accept-Encoding: br, gzip

The server could select JSON, German, and Brotli if all three are supported.

HTTP/1.1 200 OK
Content-Type: application/json
Content-Language: de
Content-Encoding: br
Vary: Accept, Accept-Language, Accept-Encoding

The response headers make the selected representation characteristics explicit and help intermediaries understand which request headers can affect the response.

Content Negotiation Checklist

  • Define which representations each endpoint supports.
  • Use Accept when clients need to express response format preferences.
  • Use Content-Type to describe request and response bodies.
  • Use Accept-Language when language negotiation is appropriate.
  • Use Accept-Encoding and Content-Encoding for content coding negotiation.
  • Use quality values when clients need to express relative preferences.
  • Return 406 when no acceptable representation can be provided and that response is appropriate.
  • Return 415 when the request content uses an unsupported media type in the relevant context.
  • Use Vary when request headers affect representation selection and caching.
  • Test negotiated responses through browsers, API clients, and intermediaries where relevant.

Helpful HTTP Tools

Several types of developer tools can make content negotiation easier to inspect and troubleshoot. Content-Type lookup tools help identify media types, MIME type detectors can verify file-related formats, and HTTP header tools make request and response headers easier to inspect.

  • Content-Type lookup tools for checking media type values.
  • MIME type detectors for identifying likely media types.
  • HTTP header generators for creating test request headers.
  • HTTP header viewers for inspecting request and response metadata.
  • HTTP response formatters for making API responses easier to inspect.

Frequently Asked Questions

What is HTTP content negotiation?

Content negotiation is the process of selecting an appropriate representation of a resource based on information supplied by the client and the representations supported by the server.

What does the Accept header do?

Accept tells the server which media types the client can accept or prefers for the response, such as application/json or text/html.

What is the difference between Accept and Content-Type?

Accept describes acceptable response media types, while Content-Type describes the media type of the message body being sent.

What does Accept-Language do?

Accept-Language communicates the client's preferred natural languages, allowing a server to select an appropriate localized representation when supported.

What does Accept-Encoding do?

Accept-Encoding tells the server which content codings the client can decode, such as gzip or Brotli. The selected coding is normally identified by Content-Encoding in the response.

Why is Vary important for content negotiation?

Vary tells caches which request headers influenced the selected representation. This helps prevent an intermediary from incorrectly reusing one representation for requests with different preferences.

What is a 406 Not Acceptable response?

A 406 response can indicate that the server cannot provide a representation that satisfies the client's stated preferences.

Does every API need content negotiation?

No. An API that intentionally supports one representation, such as JSON, can keep its behavior simple. Negotiation becomes useful when clients need to choose between multiple representations or other representation characteristics.

Conclusion

HTTP content negotiation allows clients and servers to work with different representations of the same resource. The client can communicate preferences through headers such as Accept, Accept-Language, and Accept-Encoding, while the server describes the selected representation with headers such as Content-Type, Content-Language, and Content-Encoding.

The distinction between Accept and Content-Type is especially important: Accept describes what the client wants to receive, while Content-Type describes the media type of the message body. Quality values allow clients to express relative preferences, and Vary helps caches correctly handle responses whose representations depend on request headers.

Content negotiation is not required for every API, but when multiple representations, languages, or content codings are supported, understanding these HTTP mechanisms helps create APIs that are predictable, interoperable, and easier to cache and debug.

Found an issue?

Found an error, outdated information, or something missing from this article? Let me know through the Contact page.

Your feedback helps improve our articles and keep them accurate and useful.