REST API 04 ๐ Request and Response Headers (Content-Type, Accept, User-Agent)
HTTP headers are the metadata layer of a REST API. They carry information that is not part of the resource representation itself: what format the body is in, what formats the client can accept, who the client is, and how intermediaries should handle the message. For the three headers in this chapter, the distinction is precise. Content-Type describes the body that is actually being sent. Accept describes the body the client is willing to receive. User-Agent describes the software making the request .
The separation between Content-Type and Accept is the foundation of content negotiation. A client sending a JSON body sets Content-Type: application/json to tell the server how to parse it. A client that wants a JSON response sets Accept: application/json to tell the server what format to return . When the server cannot satisfy the Accept header, the correct response is 406 Not Acceptable. When the client sends a body whose Content-Type the server cannot process, the correct response is 415 Unsupported Media Type .
Key point: Content-Type is a request header when the client sends a body, and a response header when the server returns one; Accept is a request header that declares the client’s preferred response formats and can trigger 406 Not Acceptable; User-Agent is a request header that identifies the client software and is used for analytics, tracing, and tailoring responses, but it is not a reliable capability-detection mechanism.
Why these headers matter in REST
The parsing problem. A server receiving a POST request with a body needs to know how to parse it. JSON, XML, form-encoded data, and multipart uploads are all structurally different. Content-Type is the declaration that tells the server which parser to use. Without it, the server must guess, and guessing is how JSON bodies get parsed as form data and produce empty fields .
The negotiation problem. A single resource can have multiple representations. A user record might be available as JSON, XML, or even HTML. The client declares its preference with Accept, and the server chooses the representation that best matches. If the client sends Accept: application/json and the server only has XML, the server should return 406, not an XML body that the client cannot parse .
The identification problem. The User-Agent header is the client’s self-identification. It is used for analytics (which browsers are visiting), tracing (which version of an SDK is calling the API), and tailoring (serving a mobile-optimized response to a mobile client). The W3C notes that the header value has no official format, and it is “merely a helpful fingerprint” rather than a reliable capability declaration . A server should not assume that a User-Agent string guarantees a specific feature.
The intermediary problem. Proxies, caches, and gateways read these headers. A cache may serve a stored response only if the Content-Type matches what the client expects. A proxy may reject a request whose Accept header it cannot satisfy. The headers are the contract that allows intermediaries to make routing and caching decisions without understanding the resource itself.
a. Content-Type: the body format declaration
Content-Type identifies the media type of the body. In a request, it tells the server how to parse the body the client is sending. In a response, it tells the client how to parse the body the server is returning .
POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"name": "Alice",
"email": "alice@example.com"
}
The value is a media type, optionally followed by parameters like charset. For JSON, the standard value is application/json. For form data, it is application/x-www-form-urlencoded. For file uploads, it is multipart/form-data; boundary=.... The charset parameter specifies the character encoding, which matters for text formats: Content-Type: text/html; charset=utf-8 .
A server that receives a POST or PUT without a Content-Type header cannot reliably parse the body. The correct response is 415 Unsupported Media Type, not a best-effort guess. The Oracle API documentation is explicit: Content-Type is required for POST and PUT requests, and an incorrect or missing value results in 415 .
On the response side, Content-Type is mandatory when there is a body. A 200 OK with a JSON body must include Content-Type: application/json. A 204 No Content has no body and does not need it. The header is what allows the client to decide whether to call JSON.parse or an XML parser on the response text.
b. Accept: the response format preference
Accept is a request header that lists the media types the client can handle. The server uses it to choose the response format. The simplest form is a single media type:
GET /users/42 HTTP/1.1
Host: api.example.com
Accept: application/json
The header can list multiple types with quality values (q), which express relative preference. A q of 1 is the highest priority, and lower values are less preferred. A value of 0 means “not acceptable” .
Accept: application/json, application/xml;q=0.9, text/html;q=0.8
This client prefers JSON, will accept XML, and will accept HTML as a last resort. The server should choose JSON if available, then XML, then HTML. If none of the listed types is available, the correct response is 406 Not Acceptable .
The Accept header is also used for media type versioning. A client can request a specific version of a resource by using a custom media type: Accept: application/vnd.contoso.v1+json requests version 1 of the resource in JSON format . This is a more granular form of content negotiation, and it places the version information in the header rather than the URI.
A common mistake is to omit Accept entirely. When the header is absent, the server may return any format it chooses, which is often application/json in modern APIs but is not guaranteed. The Oracle documentation states that when Accept is not specified, it defaults to the server’s preferred representation . A client that needs a specific format should always set the header explicitly.
c. User-Agent: the client identification string
User-Agent is a request header that identifies the client software. Its value is a free-form string, and the W3C notes that it has “no official format or structure” . A typical value contains the browser name and version, the operating system, and sometimes the rendering engine:
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0
The header is used for three purposes. Analytics: counting how many requests come from each browser or SDK version. Tracing: identifying which client is making a problematic request. Tailoring: serving a mobile-optimized response when the client is a mobile browser .
The header is not a reliable capability detector. A User-Agent string that contains “Mobile” does not guarantee that the device has a touchscreen. A string that contains “Chrome” does not guarantee that a specific JavaScript API is available. The W3C documentation describes pattern-matching on the string as “a crude but simple approach” and recommends against relying on it for capability inference . Servers that need to know client capabilities should use feature detection or the Client Hints mechanism, not the User-Agent string.
Modern browsers are reducing the information in the User-Agent header to prevent fingerprinting. The Sec-CH-UA family of Client Hints is the replacement: it provides structured, privacy-preserving information about the client’s brand, platform, and architecture . For new APIs, the Client Hints are the forward-looking mechanism, and the User-Agent string is increasingly treated as a legacy header.
Complete Example Session
The following session traces a request-response cycle where the client declares its body format, its response preference, and its identity, and the server responds with the correct headers.
// ============================================
// PART 1: THE REQUEST WITH ALL THREE HEADERS
// ============================================
POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json # Body is JSON; parse accordingly
Accept: application/json # Client wants JSON back
User-Agent: MyApp/2.1.0 (Node.js 22) # Client identifies itself
{
"name": "Alice",
"email": "alice@example.com"
}
// ============================================
// PART 2: THE SERVER RESPONSE
// ============================================
HTTP/1.1 201 Created
Location: /users/42
Content-Type: application/json # Response body is JSON
# (Accept was satisfied)
{
"id": 42,
"name": "Alice",
"email": "alice@example.com"
}
// ============================================
// PART 3: UNSUPPORTED BODY FORMAT (415)
// ============================================
POST /users HTTP/1.1
Content-Type: application/xml # Server only parses JSON
<user><name>Bob</name></user>
HTTP/1.1 415 Unsupported Media Type # Server cannot parse the body
// ============================================
// PART 4: UNSUPPORTED RESPONSE FORMAT (406)
// ============================================
GET /users/42 HTTP/1.1
Accept: application/xml # Server only returns JSON
HTTP/1.1 406 Not Acceptable # Server cannot satisfy Accept
// ============================================
// PART 5: MULTIPLE ACCEPT TYPES WITH QUALITY
// ============================================
GET /users/42 HTTP/1.1
Accept: application/xml;q=0.9, application/json;q=1.0
// The server prefers JSON (q=1.0) over XML (q=0.9)
// and returns application/json
The five parts demonstrate the three headers in action: the request declares its body format with Content-Type, its response preference with Accept, and its identity with User-Agent. The server returns the matching Content-Type on success, 415 when the body format is unsupported, and 406 when the response format is unsupported. The quality values show how a client can express preference order rather than a single format.
Quick Reference
Header Roles
| Header | Direction | Purpose |
|---|---|---|
Content-Type | Request + Response | Media type of the body |
Accept | Request | Preferred response media types |
User-Agent | Request | Client software identification |
Common Media Types
| Type | Use |
|---|---|
application/json | JSON body |
application/xml | XML body |
application/x-www-form-urlencoded | HTML form data |
multipart/form-data | File upload |
text/html | HTML page |
Error Responses
| Situation | Status |
|---|---|
| Request body format unsupported | 415 Unsupported Media Type |
| Response format cannot be satisfied | 406 Not Acceptable |
Missing Content-Type on body | 415 (or 400) |
Quality Values in Accept
| Syntax | Meaning |
|---|---|
type;q=1.0 | Most preferred |
type;q=0.9 | Less preferred |
type;q=0 | Not acceptable |
Best Practices
โ Do This:
POST /users
Content-Type: application/json # Declare body format
Accept: application/json # Declare response preference
User-Agent: MyApp/1.0 # Identify the client
GET /users/42
Accept: application/json # Always set Accept for known formats
โ Don’t Do This:
POST /users
# No Content-Type # Server cannot parse the body
GET /users/42
Accept: */* # Loses the ability to enforce format
User-Agent: Mozilla/5.0 (Mobile) # Assuming "Mobile" guarantees capabilities
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Empty JSON body on server | Missing Content-Type | Set application/json |
406 unexpectedly | Accept not supported | Check server-supported types |
415 on upload | Wrong Content-Type | Use multipart/form-data |
| User-Agent parsed for capabilities | Assumed reliable | Use feature detection or Client Hints |
| Missing charset | UTF-8 assumed | Set charset=utf-8 for text |
Real-World Examples
1. JSON body declaration
Content-Type: application/json
2. JSON response preference
Accept: application/json
3. Multiple acceptable types
Accept: application/json, application/xml;q=0.9
4. Versioned media type
Accept: application/vnd.contoso.v1+json
5. Form body
Content-Type: application/x-www-form-urlencoded
6. File upload
Content-Type: multipart/form-data; boundary=abc123
7. Client identification
User-Agent: MySDK/2.1.0 (Node.js 22)
8. Response body type
HTTP/1.1 200 OK
Content-Type: application/json
9. Unsupported body
HTTP/1.1 415 Unsupported Media Type
10. Unsupported accept
HTTP/1.1 406 Not Acceptable
Visual
Request vs Response Headers
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ REQUEST โ
โ Content-Type: describes the request body โ
โ Accept: describes the desired response โ
โ User-Agent: identifies the client โ
โ โ โ
โ โผ โ
โ SERVER โ
โ โ โ
โ โผ โ
โ RESPONSE โ
โ Content-Type: describes the response body โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Content Negotiation
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Client: Accept: application/json โ
โ โ โ
โ โผ โ
โ Server has JSON? โ
โ โโ YES โ 200 OK + Content-Type: application/json โ
โ โโ NO โ 406 Not Acceptable โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Body Parsing Decision
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Request body received โ
โ โ โ
โ โผ โ
โ Content-Type present? โ
โ โโ NO โ 415 Unsupported Media Type โ
โ โโ YES โ Can server parse it? โ
โ โโ NO โ 415 โ
โ โโ YES โ parse and process โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
User-Agent Purpose
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ User-Agent: MyApp/2.1.0 (Node.js 22) โ
โ โ โ
โ โโโโโโดโโโโโ โ
โ โ โ โ
โ โผ โผ โ
โ Analytics Tracing โ
โ (counts) (debugging) โ
โ โ
โ NOT for capability detection โ
โ (use feature detection / Client Hints) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
Content-Type | Body media type (request and response) |
Accept | Preferred response media types (request) |
User-Agent | Client software identification (request) |
| Unsupported body | 415 Unsupported Media Type |
| Unsatisfiable Accept | 406 Not Acceptable |
| JSON type | application/json |
| Quality values | q=1.0 (preferred), q=0 (unacceptable) |
| User-Agent reliability | Low; use Client Hints for capabilities |
Key takeaways:
Content-Typedescribes the body;Acceptdescribes the desired response. They are not interchangeable. A client sendsContent-Typeto tell the server how to parse the body it is sending, andAcceptto tell the server what format it wants back .415and406are the error codes for format mismatches. A415means the server cannot parse the request body. A406means the server cannot produce a response in any of the formats the client accepts .- Quality values allow preference ordering.
Accept: application/json, application/xml;q=0.9tells the server to prefer JSON but accept XML if JSON is unavailable . User-Agentis for identification, not capability detection. The string has no guaranteed format, and inferring device capabilities from it is unreliable. Use feature detection or Client Hints instead .- The
Content-Typeon the response must match what was actually returned. If the server returns JSON, the response must sayapplication/json, regardless of what the client requested. TheAcceptheader is a preference; the response header is a fact.
Remember: Headers are the contract between client and server. Content-Type says what the body is. Accept says what the client wants. User-Agent says who the client is. The server reads them to parse, negotiate, and identify. The client reads the response Content-Type to parse the response. When the contract is violatedโa JSON body with no Content-Type, a response in a format the client cannot parseโthe status codes (415, 406) are how the server reports the mismatch. These three headers are not optional decoration. They are the mechanism that makes content negotiation and correct parsing possible in a REST API.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!