When serving a single-page application that uses the History API, should HTTP content negotiation be used?

133 Views Asked by At

When serving a single-page application (SPA) that uses the History API, it's common practice to serve the application's HTML instead of a 404 response for requests to any unknown resource. If the application experiences some kind of full-page reload, this allows it to continue presenting the same content to the user.

This can have some negative consequences. For example, an <img> element with a typo in its src attribute will probably get served HTML content instead of a 404. It's possible to avoid this particular case by utilizing HTTP content negotiation, and only serving the HTML when the request headers indicate that the client can accept it.

However, I'm concerned that this is actually a misuse of content negotiation. As I understand it, the main purpose of content negotiation is to provide different representations of the same resource, not to determine whether a resource exists at all.

If you did implement content negotiation for SPA serving it's probably more appropriate to use a 406 Not Acceptable response, rather than a 404 Not Found. But MDN at least seems to indicate that a 406 response is generally a bad idea:

In practice, this error is very rarely used. Instead of responding using this error code, which would be cryptic for the end user and difficult to fix, servers ignore the relevant header and serve an actual page to the user. It is assumed that even if the user won't be completely happy, they will prefer this to an error code.

If a server returns such an error status, the body of the message should contain the list of the available representations of the resources, allowing the user to choose among them.

The most popular implementation of this pattern for JavaScript servers seems to be connect-history-api-fallback. At the time of writing, it uses content negotiation to determine whether to serve the SPA HTML. It doesn't seem to use 406 responses though, instead opting for 404s.

So with all of the above in mind, my questions are:

  1. What is the correct way to serve the HTML for a single-page app?
  2. Should HTTP content negotiation be involved at all?

Additionally, if content negotiation is a desirable solution here, then in the case that an unknown resource is requested, but the client has indicated that HTML is not acceptable:

  1. Should a 406 response be favoured over a 404 response?
  2. What should the body of the response contain?
  3. What additional header content is required to ensure that the system is well-behaved? (For example, I expect that I would probably at least need to set a Vary header to ensure that HTTP caching works correctly)
1

There are 1 best solutions below

0
Evert On

I feel the only right way to solve this is to not do a catch-all for every possible route, and instead correctly use 404's and serve HTML when there's actually a page to be served.