How HTTP Works

Request header

> Accept: text/html,application/json;q=0.9

Accept Header

Learn how the Accept header tells servers which content types (JSON, HTML, XML) your client can handle. Master content negotiation and quality values.

Reviewed 4 min readbeginner2 sourcesTry itMarkdown
Direction
Request
Category
Content negotiation
Spec
RFC 9110 §12.5.1
JS can set it
Yes
CORS-safelisted
Yes
On this page

TL;DR: Accept is a request header that tells the server which response formats you can handle, such as Accept: application/json. Rank alternatives with q-values (text/html, application/json;q=0.9). It’s a request, not a contract: the server may send something else or a 406, so check the response’s Content-Type before you parse it.

What is Accept?

Accept is how a client asks for a response format. Content-Type labels the format that was actually sent. A request with no Accept header means the client will take any media type.

The two headers point in opposite directions. A POST can send a JSON body and ask for a plain-text reply:

Content-Type: application/json
Accept: text/plain

Accept only affects the response. The server still parses the request body according to its Content-Type.

How Accept Works

Here’s a request (the examples on this page are trimmed):

GET /report HTTP/1.1
Host: example.com
Accept: application/json, text/csv;q=0.8

The server should pick JSON, or CSV if it can’t produce JSON. If it has nothing acceptable, it can either return 406 or ignore the header and send a default. RFC 9110 §12.5.1 allows both.

Syntax and Quality Values

Accept: text/html, application/json;q=0.9, */*;q=0.1

A missing q means 1, and q=0 means “not acceptable”. Values go from 0 to 1 with at most three decimal places. Order doesn’t matter: two entries with the same q-value are equally preferred, whichever comes first.

When a representation matches several entries, the most specific one sets its quality. Here plain text gets quality zero, while every other text type gets one:

Accept: text/*;q=1, text/plain;q=0

Common Examples

Request JSON with a plain-text fallback:

curl -i -H 'Accept: application/json, text/plain;q=0.5' https://example.com/report

Point it at an endpoint that actually implements both formats; asking for JSON won’t make a server that only speaks HTML produce it.

Content Type Categories

Media types take the form type/subtype. Parameters such as charset can narrow a match further. q is the exception: it states your preference and says nothing about the content.

ValueRequested representation
application/jsonJSON
text/htmlHTML
text/csvCSV
image/pngPNG image

If a server returns JSON labelled text/html, that’s a server bug, and clients will treat it as HTML. Trust the response’s Content-Type, not the URL’s file extension.

Wildcards

image/* matches any image subtype, and */* matches anything. Adding */*;q=0.1 gives the server a low-priority fallback. Leaving it out won’t stop a server that ignores Accept anyway.

Accept: image/png, image/*;q=0.8, */*;q=0.1

PNG gets quality 1, any other image type 0.8, and anything else 0.1. The server picks the best of the formats it can actually produce.

Real-World Scenarios

If one URL serves both JSON and HTML, add Vary: Accept to the negotiated responses so caches key on that request header. See Vary.

Skip it and a shared cache may hand the last visitor’s HTML to an API client that asked for JSON. For the /status route below, request both formats it implements:

curl -i -H 'Accept: application/json' https://example.com/status
curl -i -H 'Accept: text/plain' https://example.com/status

Both responses should list Accept in Vary, including the one in the server’s default format.

Server Response Strategies

Parse the header with a negotiation library. A substring check like accept.includes('application/json') gets q=0, wildcards and a missing header wrong.

In Express, res.format() handles routes with several serializers. Drop this into an existing app:

app.get('/status', (req, res) => {
  res.vary('Accept')
  res.format({
    'application/json': () => res.json({ status: 'ready' }),
    'text/plain': () => res.send('ready\n'),
    default: () => res.status(406).end()
  })
})

Make the fallback explicit. When the request has no Accept header, res.format() runs the first handler; that’s your app’s default, not something the client asked for.

Getting Accept right

For finer control, req.accepts() picks from the formats your route actually implements:

app.get('/report', (req, res) => {
  res.vary('Accept')
  const format = req.accepts(['application/json', 'text/plain'])
  if (format === 'application/json') return res.json({ status: 'ready' })
  if (format === 'text/plain') return res.type('text/plain').send('ready\n')
  return res.status(406).end()
})

Common Patterns

On the client, send the format you want, then check the response Content-Type before decoding. Accept asks; it doesn’t validate what comes back.

In browser JavaScript, check the status first. Error responses often come in a different media type from successful ones:

const response = await fetch('/report', {
  headers: { Accept: 'application/json' }
})
if (!response.ok) throw new Error(`HTTP ${response.status}`)
const type = response.headers.get('Content-Type')?.split(';')[0].trim()
if (type !== 'application/json') throw new Error(`Unexpected type: ${type}`)
const report = await response.json()

This client accepts only application/json on purpose. Add other media types when the API contract says they’re supported.

Browser Defaults

Browsers send different values for page navigations, images and other requests, so there’s no single browser default to copy into an API client. To see what was sent, open Network → request → Request Headers for the request you’re debugging. MDN lists example values by request context.

Testing Accept Headers

Test the rejection paths, not only the happy one. The last command removes the header that curl sends by default:

curl -i -H 'Accept: application/json;q=0, text/plain;q=1' https://example.com/report
curl -i -H 'Accept: application/pdf' https://example.com/report
curl -i -H 'Accept:' https://example.com/report

For each, look at the status, Content-Type and Vary together.

Frequently asked questions

What is the Accept header?

Accept tells the server which content types the client can handle. The server uses this for content negotiation to choose the best response format.

How do I request JSON from an API?

Send Accept: application/json. The server should return JSON if it supports it. Some APIs require this header; others default to JSON.

What does the quality value (q) mean?

Quality values indicate preference from 0 to 1. Accept: text/html, application/json;q=0.9 prefers HTML but accepts JSON. Higher q means higher preference.

What happens if the server cannot match Accept?

The server can return 406 Not Acceptable, or ignore Accept and send its default format. Both are allowed, so check the status and Content-Type of what comes back.

Sources

  1. MDN Web Docs: Acceptdeveloper.mozilla.org
  2. RFC 9110: Acceptrfc-editor.org

Keep going

Browse /search