Prompt and context
This general technical question tests HTTP representations, content negotiation, and shared-cache boundaries. The goal is to prove that a proxy cannot reuse one language or encoding for another request while keeping fallback and troubleshooting explainable.
What the interviewer evaluates
- Whether you distinguish a resource URI from its representations and understand proactive negotiation headers.
- Whether you use Vary to declare request fields that influence a response and derive cache-key and hit-rate trade-offs.
- Whether language, media type, and encoding fallback are deterministic instead of producing unexplained 406 responses.
- Whether you recognize shared-cache and privacy risks from cookies, authorization, user traits, and high-cardinality headers.
Clarifying questions to ask
Confirm whether the resource is publicly shareable, which variant dimensions exist, whether server-driven negotiation is allowed, whether clients can choose a language explicitly, and how the CDN shards by Vary. Ask about unsupported-language fallback, allowed encodings, personalization, and the acceptable cache hit rate.
30-second answer framework
I would model the resource URI separately from representation selection, choose deterministically from weighted Accept, Accept-Language, and Accept-Encoding values, and return accurate Content-Type, Content-Language, Content-Encoding, and Vary. A shared cache reuses only the same variant key; personalized or high-cardinality requests bypass it. When no representation matches, apply a documented fallback or 406 policy, log the decision, and test cache hits, bytes, and privacy with a request matrix.
Step-by-step deep dive
1. Define representations and negotiation dimensions
Treat one URI as a resource with representations such as HTML versus JSON, Chinese versus English, or gzip versus br. Include only request fields that change response bytes or semantics; do not add every cookie or the full User-Agent for convenience. Record the selection so identical normalized inputs produce the same representation.
2. Design weights, fallback, and errors
Parse quality factors and wildcards in the Accept family and define the server-supported list and priority. Language can fall back from region to base language to a default; media types can have an explicit default. Return 406 only when the contract requires rejecting every unacceptable representation. Choose compression only from an allowlist and never let unknown values bypass it.
3. Make cache variants computable
List the selection headers in Vary, for example Accept, Accept-Language, Accept-Encoding. Build a variant key from the URI, method, and normalized values of those fields; normalization covers case, whitespace, and ordering so equivalent spellings do not create unbounded keys. If authorization, session state, or user cookies affect the response, use a private cache or explicitly prevent sharing.
4. Control hit rate, privacy, and variant count
Every dimension splits the cache and lowers hit rate. Accept-Language can also become a browser-fingerprinting signal. Public pages can map long-tail languages to a finite supported set; personalized data uses private, short-lived, or no caching. Do not use Vary: * to hide unknown dependencies because it prevents shared reuse.
5. Design observability and verification
Log normalized negotiation inputs, selected representation, Vary, cache status, and fallback reason without recording full sensitive cookies. Test each variant’s headers, decompression, CDN hit and miss behavior, and especially the sequence “Chinese first, English second.” During incidents compare Age, cache status, Content-* headers, and actual bytes, then replay a fixed request matrix.
Strong sample answer
I would enumerate a finite representation set and choose deterministically from the quality factors in Accept, Accept-Language, and Accept-Encoding. The response returns Content-Type, Content-Language, Content-Encoding, and accurate Vary; the cache key contains only those normalized dimensions. Authorization, session state, and high-cardinality traits stay out of shared caches, while long-tail public languages fall back to a default. If nothing is acceptable, the contract chooses 406 or an explicit default. Logs capture choice, fallback, and hit status without sensitive cookies, and a matrix such as “Chinese then English, br then gzip” proves that the CDN never reuses the wrong variant.
Common mistakes
- Setting Content-Language but omitting Vary for a request field that changed the response.
- Putting full cookies, User-Agent, or arbitrary headers into the cache key and destroying hit rate.
- Returning 406 for every unsupported language without region, base-language, and default fallback.
- Letting a shared cache reuse a response that depends on Authorization or session data.
- Ignoring encoding negotiation and returning a Content-Encoding the client cannot decode.
- Looking only at status codes instead of Content-* headers, cache state, and actual bytes.
Follow-up questions and answers
Why can’t Vary contain only Accept-Language?
If media type or compression also changes the response, the cache needs those dimensions too. Omitting them lets a shared cache deliver an incompatible representation, causing mixed content or decode failures.
When is Vary: * useful?
It says selection depends on information not listed and normally prevents shared reuse. It can be a conservative guard, but it should not replace tracing the real dependency and choosing private caching.
How do you prevent Accept-Language key explosion?
Map client languages to a finite supported set, falling back by region, base language, and default. Where appropriate, use an explicit URL or user choice to override automatic negotiation and monitor hit rate per variant.
Should negotiation failure return 406 or a default representation?
The interface contract decides. Machine APIs often need an explicit error, while a web page may fall back to a default language or media type. Document and log the same rule; do not silently return a representation with the wrong semantics.