Foundations: HTTP content negotiation
Content negotiation connects request preferences, deterministic representation selection, fallback, response metadata, Vary, validators, privacy, and real-cache verification.
A URL identifies a resource. The bytes returned for it can still vary.
A client may prefer JSON to HTML, Brotli to identity encoding, Spanish to English, or one image format to another. The origin might select a representation, tell the client what it chose, and teach shared caches which request fields changed that choice.
RFC 9110 distinguishes proactive negotiation, where the server selects from request preferences, from reactive negotiation, where the user agent chooses after an initial response. It also defines Accept fields and Vary, whose cache-key consequences are central to correctness.
The dangerous bugs are rarely in parsing one Accept header. They live in defaults, wildcard and q-value behavior, silent fallback, missing Vary, validators shared across variants, privacy-sensitive preference headers, and error bodies negotiated differently from success.
A negotiation contract should make the selection algorithm predictable enough to test without pretending every preference must be satisfied.
Accept, Accept-Language, Accept-Encoding, quality weights, wildcards, and absence express what the client can use or prefers.
Resource variants, server capability, policy, defaults, and unacceptable cases produce a deterministic representation.
Content-Type, Content-Language, Content-Encoding, Vary, validators, and links keep clients and caches aligned.
Separate resource from representation
The stable resource identity should remain clear while media type, language, encoding, profile, and version describe alternate representations rather than accidental different objects.
I would pressure-test that decision with four questions:
- What does the URI identify?
- Which axes can vary?
- Do variants preserve meaning?
- When should a new URI exist?
The failure mode here is using negotiation to hide fundamentally different operations behind one endpoint. In HTTP APIs, web pages, downloads, documentation, media delivery, localization, compression, and error responses where one resource can have several representations selected through request preferences and server capability, that can hide the exact boundary a reviewer or teammate needs to understand. My working artifact would be a resource-to-representation map. I want it close enough to the implementation that it can change the work, not created afterward to decorate the story.
The result I would look for is clients and caches agreeing which alternatives represent the same resource. That is a narrower claim than saying the whole system improved, but it is also one I can verify and defend.
In practice, I would put a resource-to-representation map beside the question “What does the URI identify?” before the first implementation review. The next pass would use “Which axes can vary?” to test the boundary, then “Do variants preserve meaning?” to expose the state most likely to be missed. I would keep “When should a new URI exist?” for the release check because it asks whether the decision still holds outside the ideal path. The work is ready to move when the artifact can explain the choice and the observed result supports clients and caches agreeing which alternatives represent the same resource.
Parse preference grammar correctly
Comma lists, media ranges, language ranges, parameters, wildcards, q-values, absent fields, q=0, duplicates, and invalid syntax need tested handling or a trusted implementation.
The practical review starts here:
- What does absence mean?
- Which value forbids a variant?
- How are ties resolved?
- What invalid input is tolerated?
Those questions keep splitting Accept on commas and choosing the first string from becoming the default. I would capture the decision in a negotiation parser fixture table, then use it while the work is still cheap to change. For HTTP resources whose representation choices remain correct through caches and clients, the artifact should make ownership, constraint, and next action visible without requiring a private explanation.
Success would look like standard examples and adversarial header cases selecting predictably. If I cannot point to that evidence, I have a direction, not a finished decision.
The implementation move is to make a negotiation parser fixture table part of the working surface. I would use it to answer “What does absence mean?” while scope is still flexible, and “Which value forbids a variant?” before code or content becomes expensive to unwind. During QA, “How are ties resolved?” and “What invalid input is tolerated?” become concrete checks rather than discussion prompts. That sequence turns HTTP resources whose representation choices remain correct through caches and clients into something the team can operate and gives me a specific outcome to report: standard examples and adversarial header cases selecting predictably.
- PreferredBest supported variant
The server chooses an acceptable representation using documented precedence and emits metadata for the actual bytes.
- FallbackAcceptable but not first choice
A lower-weight or configured default may be served when policy allows; the response still declares its representation.
- No matchClient cannot use available variants
406 or a reactive choice response can expose options; silent incompatible bytes are not successful negotiation.
Publish the selection algorithm
Server preference, client weight, specificity, variant quality, cost, default language, and version policy should combine in one deterministic rule.
Before implementation, I would answer:
- Does client or server preference break ties?
- Which default applies?
- Can cost override preference?
- Is order stable?
The artifact is a representation scoring specification. Its job is to expose the tradeoff early enough that design, engineering, support, or product can disagree with something concrete. The common trap is letting framework iteration order decide the response; it moves uncertainty downstream and makes the final interface carry a problem the system never resolved.
For me, the useful receipt is the same available variants and preferences always producing the same result. That connects a representation-selection contract covering resource identity, media type, language, encoding, preference weights, defaults, unacceptable cases, Content-Type and Content-Language, Vary, cache keys, validators, privacy, and test matrices to an observable result instead of a process claim.
I would test this with one typical case and one boundary case. The typical case should make “Does client or server preference break ties?” easy to answer. The boundary should force a decision about “Which default applies?” and “Can cost override preference?.” I would record both in a representation scoring specification, including the part that stayed unresolved after the first pass. The final check, “Is order stable?,” is where the artifact earns its place: it either supports the same available variants and preferences always producing the same result, or it shows exactly why another iteration is needed.
Define fallback and rejection
Products should decide when a lower preference is acceptable, when a configured default is safe, when to expose alternatives, and when 406 is clearer than surprising content.
I would use these prompts during the working review:
- Is fallback intelligible?
- Did the client forbid it?
- Can choices be listed?
- Do errors use the same negotiation?
If the team slips into serving HTML to a client that explicitly accepts only JSON, the product can still look complete while its operating rule stays ambiguous. I would make a fallback and no-match decision table the shared reference and keep it small enough to update as evidence changes.
The standard is incompatible requests failing explicitly with a usable recovery path. That tells me whether the decision helped the product, not merely whether the document was completed.
The working sequence is small: draft a fallback and no-match decision table, review it against “Is fallback intelligible?,” implement the narrowest useful path, and then return with evidence for “Did the client forbid it?.” I would use “Can choices be listed?” to inspect product consequence and “Do errors use the same negotiation?” to decide whether the result is stable enough to ship. This keeps serving HTML to a client that explicitly accepts only JSON visible as a known risk and makes incompatible requests failing explicitly with a usable recovery path the release receipt rather than a hopeful conclusion.
| Signal | Decision | Working note |
|---|---|---|
| Accept | Media type | application/json, text/html, vendor types, parameters, wildcards, and q-values determine representation syntax and semantics. |
| Accept-Language | Natural language | Language ranges express preferences and can reveal private user context; Content-Language states the chosen audience language. |
| Accept-Encoding | Content coding | Compression changes transferred bytes and validators while preserving media semantics; identity handling and Vary remain important. |
Label the actual response
Content-Type, charset where applicable, Content-Language, Content-Encoding, links, and profile or version metadata should describe the selected bytes rather than the endpoint default.
I would pressure-test that decision with four questions:
- Which media type was sent?
- What audience language applies?
- Which coding changed bytes?
- Can a client discover alternatives?
The failure mode here is returning generic application/octet-stream or missing language metadata. In HTTP APIs, web pages, downloads, documentation, media delivery, localization, compression, and error responses where one resource can have several representations selected through request preferences and server capability, that can hide the exact boundary a reviewer or teammate needs to understand. My working artifact would be a representation response-header contract. I want it close enough to the implementation that it can change the work, not created afterward to decorate the story.
The result I would look for is clients interpreting every variant without sniffing or private knowledge. That is a narrower claim than saying the whole system improved, but it is also one I can verify and defend.
In practice, I would put a representation response-header contract beside the question “Which media type was sent?” before the first implementation review. The next pass would use “What audience language applies?” to test the boundary, then “Which coding changed bytes?” to expose the state most likely to be missed. I would keep “Can a client discover alternatives?” for the release check because it asks whether the decision still holds outside the ideal path. The work is ready to move when the artifact can explain the choice and the observed result supports clients interpreting every variant without sniffing or private knowledge.
Emit complete Vary
Every request field that can influence selection belongs in Vary unless the response is private or otherwise deliberately uncacheable; unnecessary variance also fragments caches.
The practical review starts here:
- Which header changed selection?
- Can the CDN see it?
- Does authentication change cacheability?
- Is a query override separate?
Those questions keep negotiating language but sharing one cache entry across all languages from becoming the default. I would capture the decision in a selection-to-Vary derivation, then use it while the work is still cheap to change. For HTTP resources whose representation choices remain correct through caches and clients, the artifact should make ownership, constraint, and next action visible without requiring a private explanation.
Success would look like cache probes never serving a representation selected for another preference set. If I cannot point to that evidence, I have a direction, not a finished decision.
The implementation move is to make a selection-to-Vary derivation part of the working surface. I would use it to answer “Which header changed selection?” while scope is still flexible, and “Can the CDN see it?” before code or content becomes expensive to unwind. During QA, “Does authentication change cacheability?” and “Is a query override separate?” become concrete checks rather than discussion prompts. That sequence turns HTTP resources whose representation choices remain correct through caches and clients into something the team can operate and gives me a specific outcome to report: cache probes never serving a representation selected for another preference set.
Scope validators per variant
Strong ETags identify exact representation bytes, so language and encoding variants often need distinct validators while Last-Modified and weak validation follow documented generation rules.
Before implementation, I would answer:
- Are bytes identical?
- Does compression change the validator?
- Can 304 return wrong metadata?
- Which variant did the client validate?
The artifact is a variant validator matrix. Its job is to expose the tradeoff early enough that design, engineering, support, or product can disagree with something concrete. The common trap is reusing one strong ETag for different language or encoded bytes; it moves uncertainty downstream and makes the final interface carry a problem the system never resolved.
For me, the useful receipt is conditional requests returning metadata and body variants consistently. That connects a representation-selection contract covering resource identity, media type, language, encoding, preference weights, defaults, unacceptable cases, Content-Type and Content-Language, Vary, cache keys, validators, privacy, and test matrices to an observable result instead of a process claim.
I would test this with one typical case and one boundary case. The typical case should make “Are bytes identical?” easy to answer. The boundary should force a decision about “Does compression change the validator?” and “Can 304 return wrong metadata?.” I would record both in a variant validator matrix, including the part that stayed unresolved after the first pass. The final check, “Which variant did the client validate?,” is where the artifact earns its place: it either supports conditional requests returning metadata and body variants consistently, or it shows exactly why another iteration is needed.
Respect preference privacy
Detailed language, media, device, and user-specific preference fields can increase fingerprinting or disclose context, so clients need control and origins should minimize unnecessary variance and logging.
I would use these prompts during the working review:
- Is this preference necessary?
- Could it identify the user?
- Can a coarse default work?
- How long is it logged?
If the team slips into collecting and caching every preference merely because the header exists, the product can still look complete while its operating rule stays ambiguous. I would make a negotiation privacy review the shared reference and keep it small enough to update as evidence changes.
The standard is the smallest preference surface needed for useful representation choice. That tells me whether the decision helped the product, not merely whether the document was completed.
The working sequence is small: draft a negotiation privacy review, review it against “Is this preference necessary?,” implement the narrowest useful path, and then return with evidence for “Could it identify the user?.” I would use “Can a coarse default work?” to inspect product consequence and “How long is it logged?” to decide whether the result is stable enough to ship. This keeps collecting and caching every preference merely because the header exists visible as a known risk and makes the smallest preference surface needed for useful representation choice the release receipt rather than a hopeful conclusion.
Keep error representations coherent
Authentication errors, validation problems, rate limits, redirects, and 406 responses should honor safe negotiated formats or state their own representation clearly.
I would pressure-test that decision with four questions:
- Can the client parse failure?
- Does 401 bypass negotiation?
- Which language explains recovery?
- Does Vary cover error selection?
The failure mode here is negotiating success JSON but returning an HTML proxy error. In HTTP APIs, web pages, downloads, documentation, media delivery, localization, compression, and error responses where one resource can have several representations selected through request preferences and server capability, that can hide the exact boundary a reviewer or teammate needs to understand. My working artifact would be an error negotiation matrix. I want it close enough to the implementation that it can change the work, not created afterward to decorate the story.
The result I would look for is machine clients handling failure without content sniffing. That is a narrower claim than saying the whole system improved, but it is also one I can verify and defend.
In practice, I would put an error negotiation matrix beside the question “Can the client parse failure?” before the first implementation review. The next pass would use “Does 401 bypass negotiation?” to test the boundary, then “Which language explains recovery?” to expose the state most likely to be missed. I would keep “Does Vary cover error selection?” for the release check because it asks whether the decision still holds outside the ideal path. The work is ready to move when the artifact can explain the choice and the observed result supports machine clients handling failure without content sniffing.
Test through real caches
Conformance should vary headers across origin, CDN, browser, conditional requests, authenticated paths, wildcard and q=0 cases, unsupported variants, and errors.
The practical review starts here:
- Does the cache key vary correctly?
- Can 304 cross variants?
- Is selection deterministic?
- Do headers match bytes?
Those questions keep unit-testing the selector while ignoring the deployed cache from becoming the default. I would capture the decision in a curl and CDN negotiation test suite, then use it while the work is still cheap to change. For HTTP resources whose representation choices remain correct through caches and clients, the artifact should make ownership, constraint, and next action visible without requiring a private explanation.
Success would look like end-to-end responses staying correct across preference order and cache reuse. If I cannot point to that evidence, I have a direction, not a finished decision.
The implementation move is to make a curl and CDN negotiation test suite part of the working surface. I would use it to answer “Does the cache key vary correctly?” while scope is still flexible, and “Can 304 cross variants?” before code or content becomes expensive to unwind. During QA, “Is selection deterministic?” and “Do headers match bytes?” become concrete checks rather than discussion prompts. That sequence turns HTTP resources whose representation choices remain correct through caches and clients into something the team can operate and gives me a specific outcome to report: end-to-end responses staying correct across preference order and cache reuse.
What I would show in the work
The public version needs evidence from the work itself. For this topic, the first five artifacts I would reach for are:
- a resource-to-representation map
- a negotiation parser fixture table
- a representation scoring specification
- a fallback and no-match decision table
- a representation response-header contract
I would not publish all five at equal weight. One should orient the reader, one should reveal the hardest tradeoff, and one should prove the result. The others can live in a downloadable note or appear as supporting frames. That edit matters because a representation-selection contract covering resource identity, media type, language, encoding, preference weights, defaults, unacceptable cases, Content-Type and Content-Language, Vary, cache keys, validators, privacy, and test matrices becomes harder to understand when every process detail is treated as equally important.
I would also show one rejected direction. The useful version is specific: which option looked attractive, which constraint made it wrong, and what evidence supported the narrower choice. That gives an engineering manager something real to question and keeps the case study from reading like the final answer was obvious from the beginning.
# request Accept application/json / Language es / br Normalized preference matrix, authenticated resource /reports/42, privacy policy permits language use, variant set v5.
# selection report.es.json.br / score 0.8 Server precedence media then language then encoding, JSON default, Spanish available, Brotli supported, no query override.
# response 200 / Vary Accept, Accept-Language, Accept-Encoding Content-Type application/json, Content-Language es, Content-Encoding br, variant ETag es-json-br-91, cache test isolated en.
Resource path
The practical follow-up I would build is an HTTP negotiation laboratory with request matrices, q-value parser cases, media and language variants, server selection algorithm, 300 and 406 examples, Content-Type and Content-Language checks, Vary cache experiments, ETag variants, privacy notes, CDN configuration, curl recipes, and conformance fixtures. I am treating that as a resource backlog item, not pretending the adjacent downloads below are the same artifact. The related cards cover useful pieces of the workflow today; this specific file should only be published when its examples, fields, and instructions are complete.
The first version should stay concise: context, constraint, decision, evidence, owner, and follow-up. Its value would come from helping someone repeat this exact review, not from adding another generic PDF to the site.
Review checklist
The article-specific review questions are:
- What does the URI identify?
- What does absence mean?
- Does client or server preference break ties?
- Is fallback intelligible?
- Which media type was sent?
- Which header changed selection?
- Are bytes identical?
- Is this preference necessary?
- Can the client parse failure?
- Does the cache key vary correctly?
I would add two editorial checks before publishing: can a recruiter find the point in the first minute, and can an engineer trace at least one claim to an implementation or production receipt? If either answer is no, the article needs another edit.
Implementation notes
For HTTP resources whose representation choices remain correct through caches and clients, I would write the implementation note before polish. It would name the changed surface, source of truth, owner, failure boundary, and verification path. Those details prevent the principle from floating above the actual code or operational workflow.
The proof signals I care about are specific to this article:
- cache probes never serving a representation selected for another preference set
- conditional requests returning metadata and body variants consistently
- the smallest preference surface needed for useful representation choice
- machine clients handling failure without content sniffing
- end-to-end responses staying correct across preference order and cache reuse
I would choose two or three of those signals for the first release rather than instrumenting everything. The strongest pair usually combines one direct behavior check with one operating check: a route and a data query, a keyboard path and a support state, a handler replay and a reconciliation result, or a migration count and a rendered screen.
The follow-up belongs in the note before shipping. It should say what remains temporary, what evidence would trigger another pass, and who owns that decision. That is how the first version stays intentionally narrow without making the boundary invisible.
Case-study packaging
I would structure the case-study version around the four visual lessons already established:
- Negotiation selects and labels one representation.
- Selection has several valid outcomes.
- Headers influence different representation axes.
- Cache correctness is part of the negotiation receipt.
The opening frame explains the product pressure. The middle two show the decision moving through the system. The last frame is the receipt: what was checked, what held, and what remained unresolved. That order lets the reader move from product judgment into implementation detail without reconstructing the whole project first.
I would include one caveat tied to HTTP APIs, web pages, downloads, documentation, media delivery, localization, compression, and error responses where one resource can have several representations selected through request preferences and server capability: a data limit, rollout boundary, unsupported state, external dependency, or result that is still directional. A precise caveat makes the evidence easier to trust because it shows where the claim stops.
The final test is whether the page creates a better conversation. If the artifact helps someone ask a sharper question about product judgment, implementation detail, or release proof in a live interview, it belongs in the story.
Interview angle
In an interview, I would explain this through a representation-selection contract covering resource identity, media type, language, encoding, preference weights, defaults, unacceptable cases, Content-Type and Content-Language, Vary, cache keys, validators, privacy, and test matrices. The story should start with the product pressure, then move into the system constraint, the artifact, and the proof. That order keeps the answer grounded. It also gives the interviewer several places to go deeper: data, frontend architecture, design systems, support, migration, accessibility, or release process.
The strongest version of the answer includes a tradeoff. I want to be able to say what I chose, what I left alone, and how I knew the work helped. That is more credible than presenting every project as a clean win.
The hiring signal
HTTP content negotiation is a hiring signal because it shows I can connect protocol semantics, API design, localization, caching, privacy, and operational verification instead of treating headers as decoration.
That is the level I want this site to communicate. The work should show taste, but it should also show operating judgment. It should make me look like someone who can enter a real product system, understand the messy middle, ship the useful version, and leave enough proof for the next person to trust it.
Use this after reading.
Practical downloads and templates that turn the article into something you can bring into a product review, implementation pass, or agent workflow.
Agent-Ready API Spec Template
An OpenAPI and Postman starter template for APIs that AI agents can discover, call, and recover from safely.
Front-End State Recipes
Reusable recipes for optimistic actions, loading, empty, error, data-transition, and disabled-control states.
Dependency Adoption Receipt
A reviewable receipt for package need, identity, provenance, permissions, supply-chain risk, verification, ownership, and removal.