| |

REST API 04 ๐ŸŒ Request and Response Headers (Content-Type, Accept, User-Agent)

โฑ๏ธ Estimated read: 11 min | |

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

HeaderDirectionPurpose
Content-TypeRequest + ResponseMedia type of the body
AcceptRequestPreferred response media types
User-AgentRequestClient software identification

Common Media Types

TypeUse
application/jsonJSON body
application/xmlXML body
application/x-www-form-urlencodedHTML form data
multipart/form-dataFile upload
text/htmlHTML page

Error Responses

SituationStatus
Request body format unsupported415 Unsupported Media Type
Response format cannot be satisfied406 Not Acceptable
Missing Content-Type on body415 (or 400)

Quality Values in Accept

SyntaxMeaning
type;q=1.0Most preferred
type;q=0.9Less preferred
type;q=0Not 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

PitfallWhy It HappensFix
Empty JSON body on serverMissing Content-TypeSet application/json
406 unexpectedlyAccept not supportedCheck server-supported types
415 on uploadWrong Content-TypeUse multipart/form-data
User-Agent parsed for capabilitiesAssumed reliableUse feature detection or Client Hints
Missing charsetUTF-8 assumedSet 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

ItemValue
Content-TypeBody media type (request and response)
AcceptPreferred response media types (request)
User-AgentClient software identification (request)
Unsupported body415 Unsupported Media Type
Unsatisfiable Accept406 Not Acceptable
JSON typeapplication/json
Quality valuesq=1.0 (preferred), q=0 (unacceptable)
User-Agent reliabilityLow; use Client Hints for capabilities

Key takeaways:

  • Content-Type describes the body; Accept describes the desired response. They are not interchangeable. A client sends Content-Type to tell the server how to parse the body it is sending, and Accept to tell the server what format it wants back .
  • 415 and 406 are the error codes for format mismatches. A 415 means the server cannot parse the request body. A 406 means 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.9 tells the server to prefer JSON but accept XML if JSON is unavailable .
  • User-Agent is 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-Type on the response must match what was actually returned. If the server returns JSON, the response must say application/json, regardless of what the client requested. The Accept header 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!