What is Accept header?
The server returns 406 Not Acceptable — "I can't respond in the format you're asking for".
Junior Level
Accept is an HTTP header that tells the server in what format the client wants to receive the response.
Examples:
Accept: application/json — I want JSON
Accept: application/xml — I want XML
Accept: text/html — I want HTML
Accept: */* — any format is fine
Difference from Content-Type:
- Content-Type — format of data I’m sending
- Accept — format of data I want to receive
Request example:
GET /users/1
Accept: application/json
// Server response:
{"id": 1, "name": "Ivan", "age": 30}
What happens if the server doesn’t support the requested format?
The server returns 406 Not Acceptable — “I can’t respond in the format you’re asking for”.
Middle Level
Content Negotiation in Spring
Content negotiation algorithm:
- Client sends
Accept: application/json - Spring looks for controller methods with
produces = "application/json" - Checks for an
HttpMessageConverterthat can write this type - If no match — returns 406 Not Acceptable
Quality coefficients (q-values)
The Accept header supports priority weights:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
text/html— priority1.0(default)application/xml— priority0.9*/*(everything else) —0.8- In the example above,
*/*has q=0.8 — this is a specific browser’s value. The client can set any q from 0.0 to 1.0.
- In the example above,
Why? This enables Graceful Degradation — if the server can’t serve the preferred format, it will serve an alternative. A mobile app requests application/json;q=1.0 and text/plain;q=0.5.
Performance and Caching
The Vary Header
If an endpoint returns different formats (JSON/XML) on the same URL depending on Accept, RFC 7231 requires returning Vary: Accept if the content depends on this header and the response may be cached. Without Vary, CDN/proxies may serve the wrong format.
Why? So that the cache (Nginx/CDN) doesn’t serve a JSON response to a user who requested XML.
Edge Cases
- Accept-Charset and Accept-Encoding: Don’t confuse them.
Accept— about data format,Accept-Charset— about encoding,Accept-Encoding— about compression (gzip/brotli) - Browser Wildcards: Browsers often send
Accept: */*. Spring will choose the first suitable converter (usually JSON)
Diagnostics
- 406 Not Acceptable Analysis: Spike in 406s = client requests a format you haven’t implemented (or you accidentally removed a converter)
- ContentNegotiationManager: Can configure priority: path extension (
.json) → query parameter (?format=json) →Acceptheader. At the Senior level, it’s recommended to rely only on headers
Senior Level
When Accept header is not needed
- API with a single fixed format (JSON only)
- Internal microservices — format is fixed by contract
- When Content Negotiation adds overhead without benefit
Content Negotiation — deep dive
Content Negotiation is the process by which the server determines the best response format for the client.
Spring ContentNegotiationManager
You can configure selection priority:
- Path extension (
.json) - Query parameter (
?format=json) Acceptheader
Recommendation: At the Senior level, rely only on headers. Path extensions and query parameters create caching and URL semantics problems.
Vendor MIME Types for versioning
Using application/vnd.mycompany.v1+json is a great versioning approach:
- Allows caching different API versions under a single URI
- If the cache respects the
Acceptheader, different versions won’t get mixed up
Performance
- Vary: Accept is recommended when supporting multiple formats — without it, the CDN may serve the wrong format from cache
- Vendor MIME Types allow versioning each entity independently
- Negative Caching: CDN can cache 406 responses, protecting the backend from repeated requests for unsupported formats
Diagnostics and Monitoring
- 406 Analysis: A spike in 406s means the client updated and is requesting a format you haven’t implemented yet
- Converter Order: The order of converter registration matters — Spring picks the first match
- Browser Behavior: Browsers often send
Accept: */*, which can lead to unexpected format selection. Test with real User-Agent headers
Interview Cheat Sheet
Must know:
- Accept tells the server the format the client wants to receive:
application/json,application/xml - Content-Type = what I’m sending, Accept = what I want to receive
- q-values (quality coefficients):
Accept: application/json;q=1.0,text/plain;q=0.5— priorities from 0.0 to 1.0 - 406 Not Acceptable — server doesn’t support the requested format
- Vary: Accept is mandatory when supporting multiple formats — without it, CDN serves the wrong format from cache
- Vendor MIME Types (
application/vnd.mycompany.v1+json) — API versioning approach - Spring ContentNegotiationManager: priority — path extension → query parameter → Accept header
Common follow-up questions:
- What does Vary: Accept do? — Tells the cache that the response depends on the Accept header; without Vary, CDN serves JSON instead of XML
- Why are q-values needed? — Graceful Degradation: if the server can’t serve the preferred format, it serves an alternative
- Why shouldn’t extensions (.json) be used for Content Negotiation? — They create caching and URL semantics problems
- How to version via Accept? — Vendor MIME Types:
application/vnd.myapi.v2+json, cache respects Accept
Red flags (DO NOT say):
- “Accept and Content-Type are the same” — Accept = expected response, Content-Type = request format
- “Browsers always send a specific Accept” — browsers often send
Accept: */* - “406 is a server error” — 406 = client requests a format the server doesn’t support
- “Vary: Accept isn’t needed” — without Vary, CDN may serve a JSON response to a client that requested XML
Related topics:
- [[What is Content-Type header]]
- [[How to organize REST API versioning]]
- [[What HTTP status codes do you know]]
- [[What is REST]]
- [[What is HATEOAS]]