HTTP content negotiation lets a server choose a representation of a resource using request information and its supported selection policy. Media type, language, and content encoding can all matter. A correct response needs to match the selection decision, and caches need enough information to avoid mixing variants.
This guide explains preference headers, response metadata, errors, and cache keys. The goal is a deliberate representation contract rather than assuming one Accept header guarantees a particular body or that every intermediary knows how the application chose it.
Define the variants you actually support
List supported media types, languages, and encodings for the endpoint. Keep the set aligned with application behavior and documented client needs. Do not advertise a variant the server cannot reliably produce.
Separate resource identity from representation. The same resource may be expressed as JSON or HTML, but those bodies have different consumers and security handling. A response’s content type should describe what was actually produced.
Record the selection policy and fallback behavior. Server-driven negotiation uses an implementation-specific algorithm within protocol rules. Clients should not need to guess whether an unsupported preference yields a fallback or an explicit error.
Interpret Accept as a preference contract
The Accept header describes acceptable response media types and can include quality values. Parse it through a supported implementation instead of ad hoc substring checks that ignore ranges or priorities.
Do not confuse Accept with Content-Type. Accept concerns the requested response representation, while Content-Type describes the media type of a message body. A request can send one type and accept another.
Test absent headers, wildcards, multiple types, and quality boundaries. One client with a simple application/json header does not validate the full selection behavior. Keep unsupported combinations predictable.
Choose language behavior deliberately
Accept-Language can influence a language variant, but an application may also have an explicit user preference or route. Define which source takes precedence. A browser hint should not unexpectedly override a deliberate account setting.
Avoid assuming language preference proves location, nationality, or identity. It is request information for content selection, not an authorization attribute. Keep business decisions and access controls independent.
Make fallback visible when no exact language exists. A regional variant and a base language can differ in terminology or formatting. Test the supported fallback hierarchy with readers who understand the relevant language.
Keep content encoding separate from content meaning
Accept-Encoding negotiates supported transfer representation encodings such as compression under the relevant HTTP behavior. It does not change the resource’s business meaning or replace the media-type contract.
Configure compression through the approved server or delivery layer and test client compatibility. Include decompression and resource-limit considerations for the full path. A smaller wire body can still require significant processing.
Avoid double-encoding or inconsistent metadata across proxies. The final Content-Encoding and body must agree. Inspect the actual response received by the client rather than only the origin’s intended setting.
Set Vary for relevant selection inputs
Vary tells caches which request headers influence response selection under the protocol’s caching rules. If language or encoding changes the response, the cache must not reuse one variant indiscriminately for another request.
Choose the relevant fields rather than adding every header. Excessive variation can fragment cache storage and reduce efficiency, while missing variation can serve incorrect content. Match the cache key to the actual selection policy.
Review application-specific identity and private data separately. Vary is not a substitute for private-cache or no-store policy when a response contains confidential user content. Correct variant selection and safe storage are distinct checks.
Make validators representation-aware
ETags and other validators should describe the selected representation under their intended comparison semantics. Reusing one strong validator across different bytes can create incorrect conditional responses.
Test variants through the real cache and proxy path. An origin can generate correct metadata while an intermediary rewrites encoding or mishandles a cache key. Verify the final behavior for each important variant.
Keep freshness policy deliberate. Negotiation selects a body; Cache-Control controls relevant storage and freshness behavior. Neither can be replaced by the other merely because the response varies.
Return meaningful unsupported-type errors
A 406 response can express that the server cannot supply an acceptable response representation under the chosen policy. A 415 addresses unsupported request-body media type or encoding in the relevant context. They are not interchangeable.
Keep error bodies and headers consistent with the documented API behavior. Do not leak internal rendering paths or private resource details while explaining unsupported input. Use a concise supported-type description where appropriate.
Test what clients actually do with errors and fallbacks. A silently returned HTML login page to a JSON client can become a parsing failure far from the original cause. Explicit behavior makes diagnosis easier.
Preserve security across every representation
Apply authentication, object authorization, and data minimization before selecting the representation. An alternate HTML or CSV endpoint must not reveal fields denied by the JSON path.
Use context-appropriate escaping and safe download behavior for each output type. A representation change can introduce browser execution or spreadsheet interpretation risks. Serialization alone does not provide every output safety control.
Review cross-origin and caching policies with the selected variant. The same underlying resource can have different client handling, but it should not gain broader authority through a media-type preference.
Test the selection matrix and real delivery path
Build a compact matrix covering supported types, languages, encodings, missing preferences, and unsupported requests. Verify body, Content-Type, Content-Encoding, Vary, validators, and relevant status codes together.
Include two clients with different preferences sharing the same delivery cache. Confirm one cannot receive the other’s inappropriate variant. Test private user contexts separately from ordinary public-language variation.
For a documentation resource offered as HTML and JSON, define supported media types, preserve authorization, and use a cache key that respects negotiation. The result is predictable client behavior rather than a collection of special-case header checks.
Frequently asked questions
Are Accept and Content-Type the same?
No. They describe different request or message-body concerns.
Does Vary make private content safe to cache publicly?
No. Apply the appropriate storage and privacy policy separately.
Where are negotiation headers explained?
Read MDN’s content-negotiation guide for the protocol concepts and related headers.
For a complementary workflow, read HTTP ETags: Revalidation Needs Representation Identity.