Fix sneaky 404s

Single Page Apps can show different content without loading a new page. To do so, they use click handlers on links and the History API. The History API allows to manipulate the browser session history. This way we can update the URL when showing a different page (usually called a "view" in Single Page Apps). It also makes sure the browser's back button still works as expected.

Take a look at the Single Page App in this codelab. It shows either cat or dog images and provides links to toggle between the two animals. It seems to work fine!

Unfortunately there is a subtle bug in the app. Let's take a look!

  • To preview the site, press View App. Then press Fullscreen fullscreen.
  • Click the Doggos link. Notice how the URL changed.
  • Reload the app.

You get a page with "Cannot GET /doggos" on it—a sneaky 404. It is "sneaky", because the web app seems to work fine as long as you only click on links within it. It breaks when using the URLs in a new browser window or when refreshing the page. The issue is that the server does not know how to respond to a request for these URLs. The JavaScript code in our web app is using the History API to navigate between them, but the server does not know what to do with them. Whenever the server does not know what to do for a requested URL, it responds with the HTTP status code 404. With this code the server says it hasn't found anything for the requested URL.

Search engines will not index the URLs in this case, because a user would click on a search result and find the error message, but not the content they were looking for, such as the dog pictures.

Fixing the server

This project uses an express.js server written in JavaScript. Let's fix the server, so it responds with index.html and the single page app will take care of the rest.

  • Click Remix to Edit to make the project editable.
  • Select the server.js file.

This file contains the server code. It sets up an express.js server and sends the content of index.html. The route setup in line 15 only serves the web app when requests go to the URL /. The server should also serve the other URLs we have created. Let's change this to serve all URLS, so it also works with additional URLs in the future.

To do so, we can change the code starting at line 15 to this:

app.get('/*', function(request, response) {
  response.sendFile(__dirname + '/views/index.html');
});

The /* matches any URL and the server now responds with the web app in index.html for any given URL.

  • To preview the site, press View App. Then press Fullscreen fullscreen.

Refreshing and opening the links in a new incognito window should now work as expected.