# 300 Multiple Choices

> The request has multiple possible responses. Learn when to use 300 Multiple Choices for content negotiation and alternative resource locations.

Source: https://howhttpworks.com/status-codes/300
Last reviewed: 2026-10-04

> **TL;DR:** Resource has multiple formats available (PDF, HTML, etc.). Choose your preferred option from the provided list.

## What is 300 Multiple Choices?

A **300 Multiple Choices** status code indicates that the requested resource has multiple representations available, and the client should choose one. Think of it like a menu at a restaurant offering the same dish in different sizes or preparation styles—you get to pick which variation you want.

This status code is used for content negotiation when a server can provide the same resource in different formats, languages, or locations, giving the client the power to select the most appropriate option.

## When Does This Happen?

You'll see a 300 Multiple Choices response in these situations:

**1. Multiple Format Options**

```text
/document can be:
→ /document.pdf
→ /document.html
→ /document.txt
```

**2. Language Variants**

```text
/page has translations:
→ /page/en (English)
→ /page/es (Spanish)
→ /page/fr (French)
```

**3. Resolution Options**

```text
/image.jpg available as:
→ /image-hd.jpg (1920x1080)
→ /image-sd.jpg (1280x720)
→ /image-thumb.jpg (320x240)
```

**4. Mirror Locations**

```text
/download available from:
→ /download/us-east
→ /download/eu-west
→ /download/asia-pacific
```

**5. API Version Selection**

```text
/api/resource supports:
→ /api/v1/resource
→ /api/v2/resource
→ /api/v3/resource
```

## Example Responses

**Multiple Format Choices:**

```http
HTTP/1.1 300 Multiple Choices
Content-Type: text/html
Location: /document.pdf

<!DOCTYPE html>
<html>
<head><title>Multiple Choices</title></head>
<body>
  <h1>Multiple Formats Available</h1>
  <p>This document is available in multiple formats:</p>
  <ul>
    <li><a href="/document.pdf">PDF Format</a> (Recommended)</li>
    <li><a href="/document.html">HTML Format</a></li>
    <li><a href="/document.docx">Word Document</a></li>
    <li><a href="/document.txt">Plain Text</a></li>
  </ul>
</body>
</html>
```

**Language Selection:**

```http
HTTP/1.1 300 Multiple Choices
Content-Type: application/json
Vary: Accept-Language

{
  "message": "Multiple language versions available",
  "choices": [
    {
      "lang": "en",
      "url": "/page/en",
      "title": "English Version"
    },
    {
      "lang": "es",
      "url": "/page/es",
      "title": "Spanish Version"
    },
    {
      "lang": "fr",
      "url": "/page/fr",
      "title": "French Version"
    }
  ]
}
```

**Mirror Selection:**

```http
HTTP/1.1 300 Multiple Choices
Content-Type: text/html
Link: </download/us-east>; rel="alternate"; geo="US"
Link: </download/eu-west>; rel="alternate"; geo="EU"

<!DOCTYPE html>
<html>
<head><title>Choose Download Location</title></head>
<body>
  <h1>Select Download Mirror</h1>
  <ul>
    <li><a href="/download/us-east">US East Coast</a> (Fastest for Americas)</li>
    <li><a href="/download/eu-west">EU West</a> (Fastest for Europe)</li>
    <li><a href="/download/asia">Asia Pacific</a> (Fastest for Asia)</li>
  </ul>
</body>
</html>
```

## Real-World Example

Imagine you're requesting a technical whitepaper that's available in multiple formats:

**Client Request:**

```http
GET /whitepaper HTTP/1.1
Host: docs.example.com
User-Agent: Mozilla/5.0...
Accept: */*
```

**300 Multiple Choices Response:**

```http
HTTP/1.1 300 Multiple Choices
Content-Type: text/html; charset=utf-8
Location: /whitepaper.pdf
Link: </whitepaper.pdf>; rel="alternate"; type="application/pdf"
Link: </whitepaper.html>; rel="alternate"; type="text/html"
Link: </whitepaper.epub>; rel="alternate"; type="application/epub+zip"
Content-Length: 892

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Download Options - Technical Whitepaper</title>
</head>
<body>
  <h1>Choose Your Preferred Format</h1>
  <p>This whitepaper is available in multiple formats:</p>

  <div class="format-options">
    <div class="option">
      <h2>PDF (Recommended)</h2>
      <p>Best for printing and archiving</p>
      <a href="/whitepaper.pdf">Download PDF (2.4 MB)</a>
    </div>

    <div class="option">
      <h2>HTML</h2>
      <p>Read online with interactive examples</p>
      <a href="/whitepaper.html">View HTML Version</a>
    </div>

    <div class="option">
      <h2>EPUB</h2>
      <p>For e-readers and mobile devices</p>
      <a href="/whitepaper.epub">Download EPUB (1.8 MB)</a>
    </div>

    <div class="option">
      <h2>Markdown</h2>
      <p>Source format for developers</p>
      <a href="/whitepaper.md">Download Markdown (456 KB)</a>
    </div>
  </div>
</body>
</html>
```

## 300 vs Other Redirect Codes

| Code    | Meaning           | Use Case                  | Client Choice          |
| ------- | ----------------- | ------------------------- | ---------------------- |
| **300** | Multiple choices  | Multiple valid options    | Client selects         |
| **301** | Moved Permanently | Single permanent redirect | Automatic redirect     |
| **302** | Found             | Single temporary redirect | Automatic redirect     |
| **303** | See Other         | After POST operation      | Automatic GET redirect |

## Important Characteristics

**Client-Driven Selection:**

```http
HTTP/1.1 300 Multiple Choices
↑
Server provides options, client decides
```

**Optional Location Header:**

```http
HTTP/1.1 300 Multiple Choices
Location: /document.pdf  ← Optional: Server's preferred choice

HTML body lists all options
```

**Rarely Used in Practice:**

- Most servers auto-negotiate based on Accept headers
- Direct 301/302 redirects are more common
- Modern APIs use content negotiation instead

**Content Negotiation Alternative:**

```http
Instead of 300:
Accept: application/json → 200 OK (JSON response)
Accept: text/html → 200 OK (HTML response)
```

## Common Mistakes

**❌ Using 300 instead of content negotiation**

```http
GET /data
Accept: application/json

HTTP/1.1 300 Multiple Choices  ← Bad
[List of format options]

Better: Return JSON directly (200 OK)
```

**❌ No meaningful choices**

```http
HTTP/1.1 300 Multiple Choices

<html>
  <a href="/same-content">Option 1</a>  ← All links are identical
  <a href="/same-content">Option 2</a>
</html>
```

**❌ Missing response body**

```http
HTTP/1.1 300 Multiple Choices
Location: /option1

← No body explaining choices
```

**✅ Correct usage**

```http
HTTP/1.1 300 Multiple Choices
Content-Type: text/html
Location: /document.pdf

<!DOCTYPE html>
<html>
<body>
  <h1>Choose Format</h1>
  <ul>
    <li><a href="/document.pdf">PDF</a> - Best for printing</li>
    <li><a href="/document.html">HTML</a> - Best for reading online</li>
  </ul>
</body>
</html>
```

## Getting 300 Multiple Choices right

**Provide Clear Descriptions:**

```html
<!-- Good: Explains each choice -->
<div class="choices">
  <div>
    <h3>PDF Version</h3>
    <p>Best for: Printing, offline reading, archiving</p>
    <p>Size: 2.4 MB</p>
    <a href="/doc.pdf">Download PDF</a>
  </div>

  <div>
    <h3>Interactive HTML</h3>
    <p>Best for: Online reading, searchable, mobile-friendly</p>
    <p>Includes: Interactive code examples</p>
    <a href="/doc.html">View HTML</a>
  </div>
</div>
```

**Include Machine-Readable Metadata:**

```http
HTTP/1.1 300 Multiple Choices
Link: </data.json>; rel="alternate"; type="application/json"
Link: </data.xml>; rel="alternate"; type="application/xml"
Link: </data.csv>; rel="alternate"; type="text/csv"
Content-Type: text/html

<html>...</html>
```

**Recommend a Default:**

```http
HTTP/1.1 300 Multiple Choices
Location: /document.pdf  ← Recommended default

<html>
  <p>Recommended: <a href="/document.pdf">PDF Version</a></p>
  <p>Other options: ...</p>
</html>
```

**Consider Auto-Negotiation Instead:**

```javascript
// Modern approach: Auto-negotiate based on Accept header
app.get('/document', (req, res) => {
  const acceptHeader = req.headers.accept

  if (acceptHeader.includes('application/pdf')) {
    res.redirect('/document.pdf')
  } else if (acceptHeader.includes('text/html')) {
    res.redirect('/document.html')
  } else {
    // Fall back to 300 if can't determine
    res.status(300).render('choices', { options })
  }
})
```

## Implementation Examples

**Express.js:**

```javascript
app.get('/download', (req, res) => {
  const options = [
    { url: '/download.pdf', type: 'application/pdf', label: 'PDF' },
    { url: '/download.epub', type: 'application/epub+zip', label: 'EPUB' },
    { url: '/download.mobi', type: 'application/x-mobipocket-ebook', label: 'MOBI' }
  ]

  res
    .status(300)
    .set('Location', '/download.pdf') // Default
    .set('Link', options.map((o) => `<${o.url}>; rel="alternate"; type="${o.type}"`).join(', '))
    .render('multiple-choices', { options })
})
```

**Apache .htaccess:**

```apache
# Rarely used, but possible
<Files "document">
    # Return 300 with choices
    ErrorDocument 300 /choices.html
    Header set Status "300 Multiple Choices"
</Files>
```

**Nginx:**

```nginx
location /resource {
    return 300;
    add_header Content-Type text/html;
    add_header Location /resource.pdf;

    # Serve choice page
    try_files /choices.html =404;
}
```

**Python Flask:**

```python
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/document')
def document():
    options = [
        {'url': '/document.pdf', 'format': 'PDF', 'size': '2.4 MB'},
        {'url': '/document.html', 'format': 'HTML', 'size': 'N/A'},
        {'url': '/document.txt', 'format': 'Plain Text', 'size': '45 KB'}
    ]

    response = render_template('choices.html', options=options)

    return response, 300, {
        'Location': '/document.pdf',
        'Link': '</document.pdf>; rel="alternate"; type="application/pdf"'
    }
```

## Try It Yourself

Visit our [request builder](https://howhttpworks.com/tools/playground) and see 300 Multiple Choices:

1. Set method to **GET**
2. Set path to **/multi-format-demo**
3. Click **Send request**
4. See 300 response with multiple format options
5. Explore different choice presentations

## Try it with curl

Request a resource that exists in several representations and read what the server offers. Because the choice is left to the client, you pick a URL from the list and request it yourself.

```bash
curl -i https://example.com/report
curl -i https://example.com/report.pdf
```

Example output (illustrative, not captured from a real server):

```http
HTTP/1.1 300 Multiple Choices
Content-Type: text/html
Link: </report.pdf>; rel="alternate"; type="application/pdf"

<ul><li><a href="/report.pdf">PDF</a></li><li><a href="/report.html">HTML</a></li></ul>
```

In practice 300 is rare. Most servers pick a representation with content negotiation and answer `200` or [406](https://howhttpworks.com/status-codes/406) instead.

## Related Status Codes

- [301 Moved Permanently](https://howhttpworks.com/status-codes/301) - Single permanent redirect
- [302 Found](https://howhttpworks.com/status-codes/302) - Single temporary redirect
- [406 Not Acceptable](https://howhttpworks.com/status-codes/406) - No acceptable representation available
- [200 OK](https://howhttpworks.com/status-codes/200) - Single successful response (after choice made)
