# 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.

Source: https://howhttpworks.com/headers/accept
Last reviewed: 2026-10-05

> **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](https://howhttpworks.com/headers/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:

```http
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):

```http
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](https://howhttpworks.com/status-codes/406) or ignore the header and send a default. [RFC 9110 §12.5.1](https://www.rfc-editor.org/rfc/rfc9110#section-12.5.1) allows both.

## Syntax and Quality Values

```http
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:

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

## Common Examples

Request JSON with a plain-text fallback:

```bash
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.

| Value | Requested representation |
| --- | --- |
| `application/json` | JSON |
| `text/html` | HTML |
| `text/csv` | CSV |
| `image/png` | PNG 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.

```http
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](https://howhttpworks.com/headers/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:

```bash
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()`](https://expressjs.com/en/5x/api/response/#res.format) handles routes with several serializers. Drop this into an existing app:

```javascript
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()`](https://expressjs.com/en/5x/api/request/#req.accepts) picks from the formats your route actually implements:

```javascript
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:

```javascript
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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept).

## Testing Accept Headers

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

```bash
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.

## Related Headers

- [Accept-Encoding](https://howhttpworks.com/headers/accept-encoding)
- [Accept-Language](https://howhttpworks.com/headers/accept-language)
- [Content-Type](https://howhttpworks.com/headers/content-type)
- [Vary](https://howhttpworks.com/headers/vary)
