HTTP

300 - Multiple Choices

Seeing a 300 Multiple Choices means the server found multiple representations of the resource and can't decide which one to return—different formats, languages, or versions are available. This client-side informational response (3xx) requires the client to choose. Most common when content negotiation offers multiple options, but also appears when resources exist in multiple formats, servers provide multiple versions, or Accept headers match several variants.

#Common Causes

  • Frontend: Accept header matches multiple content types. Multiple resource formats available. Content negotiation offers several options. No specific format preference.
  • Backend: Server has multiple representations of resource. Content negotiation returns multiple matches. Resource available in different formats/languages. Server can't choose default.
  • Infrastructure: CDN serves multiple variants. Load balancer routes to different formats. Reverse proxy offers multiple options.

Solutions

  1. 1Step 1: Diagnose - Check DevTools Network tab Response body—300 responses usually list available options. Review Link headers for variant URLs. Check Content-Location headers.
  2. 2Step 2: Diagnose - Server logs show which variants matched. Review content negotiation configuration. Check available resource formats. Examine Accept header matching.
  3. 3Step 3: Fix - Client-side: Specify preferred format in Accept header. Use more specific Accept values. Follow Link headers to choose variant. Implement content negotiation logic.
  4. 4Step 4: Fix - Server-side: Return 300 with Link headers listing variants. Provide Content-Location for each variant. Set default format if possible. Improve content negotiation.
  5. 5Step 5: Fix - Infrastructure: Configure CDN variant selection. Review load balancer content negotiation. Update reverse proxy variant handling.

</>Code Examples

Fetch API: Handle 300 Multiple Choices
1// Client-side: Handle 300 by choosing preferred format
2async function fetchResourceWithChoice(url) {
3  let response = await fetch(url);
4  
5  if (response.status === 300) {
6    // Multiple choices available - parse options
7    const links = response.headers.get('Link');
8    const contentType = response.headers.get('Content-Type');
9    
10    // Parse Link header for available variants
11    const variants = parseLinkHeader(links);
12    console.log('Available variants:', variants);
13    
14    // Choose based on Accept header preference
15    response = await fetch(url, {
16      headers: {
17        'Accept': 'application/json', // Specify preferred format
18      },
19    });
20    
21    // If still 300, choose first variant from Link header
22    if (response.status === 300 && variants.length > 0) {
23      const preferredVariant = variants.find(v => v.rel === 'alternate' && v.type === 'application/json') 
24                              || variants[0];
25      response = await fetch(preferredVariant.url);
26    }
27  }
28  
29  return response.json();
30}
31
32// Parse Link header
33function parseLinkHeader(linkHeader) {
34  if (!linkHeader) return [];
35  
36  const links = linkHeader.split(',');
37  return links.map(link => {
38    const parts = link.split(';');
39    const url = parts[0].trim().slice(1, -1); // Remove < >
40    const params = {};
41    
42    parts.slice(1).forEach(param => {
43      const [key, value] = param.trim().split('=');
44      params[key] = value?.replace(/"/g, '');
45    });
46    
47    return { url, ...params };
48  });
49}
50
51// Automatic format selection
52async function fetchWithAutoSelection(url, preferredFormat = 'application/json') {
53  const response = await fetch(url, {
54    headers: {
55      'Accept': preferredFormat,
56    },
57  });
58  
59  if (response.status === 300) {
60    // Use more specific Accept header
61    return fetch(url, {
62      headers: {
63        'Accept': `${preferredFormat}, */*;q=0.1`,
64      },
65    });
66  }
67  
68  return response.json();
69}
Express.js: Return 300 with Variants
1// Server-side: Return 300 with multiple choices
2const express = require('express');
3const app = express();
4
5// Resource with multiple representations
6app.get('/api/resource', (req, res) => {
7  const acceptHeader = req.headers.accept || '*/*';
8  
9  // Check if multiple formats match
10  const formats = ['application/json', 'application/xml', 'text/html'];
11  const matches = formats.filter(format => 
12    acceptHeader.includes(format) || acceptHeader === '*/*'
13  );
14  
15  if (matches.length > 1) {
16    // Multiple choices - return 300 with Link headers
17    const links = formats.map(format => {
18      const url = `/api/resource.${format.split('/')[1]}`;
19      return `<${url}>; rel="alternate"; type="${format}"`;
20    }).join(', ');
21    
22    return res.status(300)
23      .set('Link', links)
24      .set('Content-Type', 'text/html')
25      .send(`
26        <html>
27          <body>
28            <h1>Multiple Choices</h1>
29            <p>Available formats:</p>
30            <ul>
31              <li><a href="/api/resource.json">JSON</a></li>
32              <li><a href="/api/resource.xml">XML</a></li>
33              <li><a href="/api/resource.html">HTML</a></li>
34            </ul>
35          </body>
36        </html>
37      `);
38  }
39  
40  // Single match - return that format
41  if (matches.includes('application/json')) {
42    res.json({ data: 'resource' });
43  } else if (matches.includes('application/xml')) {
44    res.set('Content-Type', 'application/xml');
45    res.send('<data>resource</data>');
46  } else {
47    res.send('<html><body>resource</body></html>');
48  }
49});
50
51// Variant endpoints
52app.get('/api/resource.json', (req, res) => {
53  res.json({ data: 'resource' });
54});
55
56app.get('/api/resource.xml', (req, res) => {
57  res.set('Content-Type', 'application/xml');
58  res.send('<data>resource</data>');
59});
Nginx: Content Negotiation with Multiple Choices
1# Nginx: Handle multiple choices in content negotiation
2server {
3    listen 80;
4    server_name api.example.com;
5    
6    location /api/resource {
7        # Enable content negotiation
8        index index.json index.xml index.html;
9        
10        # If multiple formats exist, return 300
11        # Or let backend handle it
12        proxy_pass http://backend;
13        proxy_set_header Host $host;
14        proxy_set_header Accept $http_accept;
15    }
16    
17    # Or serve static variants
18    location /api/resource.json {
19        default_type application/json;
20        return 200 '{"data":"resource"}';
21    }
22    
23    location /api/resource.xml {
24        default_type application/xml;
25        return 200 '<data>resource</data>';
26    }
27}

Related Errors

Provider Information

This error code is specific to HTTP services. For more information, refer to the official HTTP documentation.

300 - Multiple Choices | HTTP Error Reference | Error Code Reference