3

How to configure CloudFront for the HTML5 history API

 2 years ago
source link: https://advancedweb.hu/how-to-configure-cloudfront-for-the-html5-history-api/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Navigation state in webapps

Modern web applications handle their navigation state internally and don’t require page reloads when the user is moving between the pages. This is a convenient feature as that also means ephemeral state, such as text entered into input fields and popups are preserved. But that also means, by default, that the URL that the users see stays the same and if they choose to reload the page they are taken back to the home page.

One solution for both of these problems was the use of fragments. This is the part that comes after the # and the webapp is free to change it and that does not cause a page reload.

// url is: example.com
window.location.hash = "articles";
// url is: example.com/#articles

And if the user reloads the page, the webapp can restore the navigation by reading the fragment:

// url is: example.com/#articles
window.location.hash; // #articles
Video course
No support for video.
Speed up your web application with Amazon CloudFront
Video course, 2020

This was the de-facto solution before HTML5 History API. The problem with fragments is that they are different. Instead of having a “normal-looking” URL, such as example.com/articles, it is example.com/#articles. Other than that, the browser does not send the fragment, so there is no way to provide useful data for clients that don’t execute Javascript, such as web crawlers.

HTML5 History API

The History API provides a solution for all the problems with fragments and it is widely supported in browsers. It provides a pushState that changes the URL in a way that does not trigger a page reload.

// url is: example.com
history.pushState(undefined, undefined, "/articles")
// url is: example.com/articles

But it also introduced a new problem: what happens if the user reloads the page?

With fragments, the HTTP request goes to the same URL for all pages. But with pushState, it’s no longer the case. Worse still, if the backend can not serve a HTML page for the changed URLs it’s not immediately apparent.

Here’s a video showing the problem:

No support for video.

This article focuses on how to configure CloudFront for this scenario but the solution described here works for other backends too.

CloudFront

Let’s consider a typical setup: the frontend is hosted behind a CloudFront distribution and that uses the HTML5 History API to provide friendly URLs. By default, refreshing the page returns an error.

browserCloudFrontbackendindex.htmlapp.jsOpen URL example.comGET /GET /index.htmlindex.htmlNavigate to /articlespushStateReload pageGET /articlesGET /articles404404

The first request works as when CloudFront forwards it to the origin the URL points to a file that exists on the backend. Navigation works as that does not trigger a new request. But refresh is broken as the new URL does not point to a file on the backend.

One solution is to configure the backend to return the index.html for URLs that has no matching file. If you use Apache, Nginx, or another custom webserver then you can check the documentation how to do it. But there are cases, such as for S3 that does not support this behavior. In that case, you need to configure CloudFront to handle it internally.

Error response

CloudFront supports the notion of “error response”. If the origin returns a specific type of error, CloudFront can automatically modify the request and return a different response.

To support HTML5 History API, configure a custom response for the 404 errors:

Free email course

cf-error-page-85ab76544a922e6d4b867592bc246b1548118dfe9e9b46760594683a7f0f4d1d.png

How this works?

CloudFront still sends a request to the backend and that responds with a 404 code. This triggers the custom error response and CloudFront sends another request to the / path. The backend responds with the index.html and CloudFront returns that with the 200 status code.

browserCloudFrontbackendindex.htmlapp.jsReload pageGET /articlesGET /articles404GET /index.htmlindex.html

Alternative: CloudFront Functions

An alternative solution is to use CloudFront Functions to dynamically rewrite the request path. This way you have more control over which paths are changed and which should generate 404 errors instead.

Related
The new way to run code on the edge provides many benefits over Lambda@Edge

browserCloudFrontbackendindex.htmlapp.jsReload pageGET /articlesRun functionrequest.uri="/"GET /index.htmlindex.html

Conclusion

The history.pushState method allows a web application to change the page URL without triggering a HTTP request. This allows nicer-looking navigation and permalinks but if the user reloads the page the request goes to the path that is shown in the URL bar. If the backend does not support it, this results a 404 error.

CloudFront offers a custom error response setting to handle these errors. Alternatively, you can use CloudFront Functions to rewrite the URL before the request is sent to the origin.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK