HTTP 300 is Multiple Choices: the target has several representations, each with its own URL, and the server wants the user or user agent to choose one (RFC 9110 section 15.4.1). That’s reactive negotiation, the reverse of the usual proactive kind, where the server picks from the request’s Accept headers. The body should list the choices, and a server with a favorite should name it in Location.
The exchange
GET /docs/manual HTTP/1.1
Host: example.com
HTTP/1.1 300 Multiple Choices
Location: https://example.com/docs/manual.en.html
Content-Type: text/html
<ul>
<li><a href="/docs/manual.en.html">English</a></li>
<li><a href="/docs/manual.de.html">Deutsch</a></li>
</ul>
Browsers don’t follow it
RFC 9110 lets a user agent follow a 300’s Location and leaves the format of the list undefined. Chrome and Firefox treat it as a page, not a redirect: the visitor sees the body, and the Location is ignored. The Fetch standard’s redirect statuses are 301, 302, 303, 307 and 308, so fetch() hands your code the 300 itself. curl -L, on the other hand, follows any 3xx that carries a Location.
RFC 9110 also tells a client to treat a 3xx code it doesn’t recognize as a 300, which is why a client older than 308 may stop at a 308 instead of following it.
Who sends it
- Apache’s mod_negotiation (MultiViews or type maps) answers 300 when several variants are equally acceptable, unless
ForceLanguagePriority Preferlets it pick one. When no variant is acceptable it sends 406, unlessForceLanguagePriority Fallbackis set. - Apache’s mod_speling answers a misspelled URL with a 300 and a list when more than one file is a close match; a single close match gets a 301.
Caching
A 300 is heuristically cacheable: with no Cache-Control or Expires, a cache may store it and reuse it. If the list of choices can change, send explicit freshness.
What to send instead
the server can choose from Accept-Language or Accept 200, with Vary on that header
one variant is the right answer for this visitor 302 to it
the content moved for good 301 or 308
When the choice depends on request headers, send Vary: Accept-Language (or whichever header you used) so caches keep the variants apart. RFC 9110 also mentions a Link header with rel="alternate" for listing alternatives, and calls its deployment a chicken-and-egg problem. For search engines, mark language versions with hreflang alternate links instead.
Search 300 308 in the tool below to compare the two.