o7planning

Configure Amazon CloudFront Error Pages

  1. Why does a 403 error appear for non-existing URLs?
  2. Where to configure error pages?
  3. Simple example handling 404 errors
  4. Advanced 404 error handling example (1)
  5. Advanced 404 error handling example (2)
In the previous lesson we created a CloudFront distribution for S3 Bucket.
In this article, we will configure to handle website-related errors that occur on CloudFront, specifically errors 403, 404,... If error pages are not configured, users may see confusing and unfriendly error messages that look like this:
Note: Handling HTTP errors (403, 404,...) can be done on the S3 Bucket if it has the "Static Website hosting" feature enabled. Anyway, the way CloudFront and S3 Bucket handle HTTP errors is completely different, and which solution to choose is up to you.
  • Amazon S3 Static Website Redirection Rules
  • Amazon S3 Rest API Endpoint vs S3 Web Endpoint

1. Why does a 403 error appear for non-existing URLs?

After creating the CloudFront distribution. Try accessing a URL that doesn't exist, and you may receive an "Access Denied" error message. This is a 403 error code, when it should be 404 (NoSuchKey / Page Not Found).
<?xml version="1.0" encoding="UTF-8"?>
<Error>
   <Code>AccessDenied</Code>
   <Message>Access Denied</Message>
   <RequestId>W1GYWM3Z8WRHPYC3</RequestId>
   <HostId>GNRyvO3x/JrA2aTLjO5TWQMYJlqNZWkigBZ0A8heMzAXkjumCICIAjtLqtAxapp0I122P0oef8U=</HostId>
</Error>
The above error appears when the user sends a request to retrieve a non-existent object, while not having permission to list objects on the S3 Bucket. To handle this issue you need to configure a policy on the S3 Bucket that allows the s3:ListBucket permission.
After configuring the policy on S3 Bucket allows users to list objects. You re-access a URL that does not exist, now you will receive a 404 error message (NoSuchKey / Page Not Found), exactly as you expected.
<?xml version="1.0" encoding="UTF-8"?>
<Error>
	<Code>NoSuchKey</Code>
	<Message>The specified key does not exist.</Message>
	<Key>123</Key>
	<RequestId>YEAC2NV5KJ89BVKG</RequestId>
	<HostId>FRligzQKzyXT486lUikuJjJ1hLDZpAtF7/MgD14oCX3oqkIgnq7ho8P9HIxDSYupRC31NictozA=</HostId>
</Error>

2. Where to configure error pages?

First, login to CloudFront.
Select a Distribution:
  • [Selected Distribution] > Error pages > Create custom error response

3. Simple example handling 404 errors

In this section we will configure the response content to return to users when they access a URL that does not exist.
On S3 Bucket create a "response-404-simple.html" object with the following content:
response-404-simple.html
<!DOCTYPE html>
<html>
<head>
  <title>404 - Page Not Found</title>
</head>
<body>
   <h1>Sorry, 404 - Page Not Found!</h1>
   Click <a href="/">here</a> to home page.
</body>
</html>
On CloudFront create an "Error response":
Http error code
404: Not Found
Customize error response
Yes
Response page path
/response-404-simple.html
HTTP Response code
404: Not Found
Finally, access a certain URL that doesn't exist and see the results. The content of "response-404-simple.html" was returned to the user.
Note: Every time you change configuration on CloudFront or data on S3 Bucket, you need to invalidate CloudFront's cache.

4. Advanced 404 error handling example (1)

Suppose your website has the following articles:
  • 11111/css-tutorial.html
  • 22222/java-tutorial.html
Sometimes users accidentally access URL(s) that don't exist, possibly due to typos, for example:
  • 11111/csstutorial.html
  • 11111/css
And you want to redirect user requests to the correct URL.
In this section, we will look at a solution illustrated below:
Configure "Error pages" for 404 errors:
Http error code
404: Not Found
Customize error response
Yes
Response page path
/response-404-advanced-1.html
HTTP Response code
404: Not Found
response-404-advanced-1.html
<!DOCTYPE html>
<html>
<head>
  <title>404 - Page Not Found</title>
  <meta name='referrer' content='no-referrer' />
  <script src="/handle-404-advanced-1.js"></script>
</head>
<body>
   <h1>Sorry, 404 - Page Not Found!</h1>
   Click <a href="/">here</a> to home page.
</body>
</html>
Javascript will redirect user requests to the correct URL(s).
handle-404-advanced-1.js
// Create Page ID Map
const pageIdMap = new Map([
   ["11111", "css-tutorial.html"], // 11111/css-tutorial.html
   ["22222", "java-tutorial.html"] // 22222/java-tutorial.html
]);

// Check Page URL and redirect to correct URL If need.
function redirectIfNeed() {
   const currentUrl = window.location.href;
   const currentPathname = window.location.pathname;

   console.log('currentPathname: ' + currentPathname);
   if (currentPathname == '/response-404-advanced-1.html') {
      console.log('Ignore for /response-404-advanced-1.html page');
      return;
   }
   // currentPathname --> /11111/suffix-text
   let j = currentPathname.indexOf('/', 1);
   let pageId = '';
   let suffixText = '';
   if (j == -1) {
      pageId = currentPathname.substring(1);
      suffixText = '';
   } else {
      pageId = currentPathname.substring(1, j);
      suffixText = currentPathname.substring(j + 1);
   }
   console.log('pageId= ' + pageId);
   console.log('suffixText= ' + suffixText);

   let correctSuffixText = pageIdMap.get(pageId);
   console.log('correctSuffixText= ' + correctSuffixText);

   if (!correctSuffixText) {
      console.log('correctSuffixText is undefined -> The Correct Page Not Found!');
      return;
   }
   if (correctSuffixText == suffixText) {
      console.log('correctSuffixText == suffixText -> The Correct Page Not Found!');
      return;
   }
   console.log('Wait 3 seconds before redirect to the correct URL...');
   // Wait 3 seconds before redirect to the correct URL
   // --> So you can see the logs in the console
   setTimeout(()=> {
      // Redirect
      window.location.href = '/' + pageId + '/' + correctSuffixText;
   } ,3000);
}

redirectIfNeed();

5. Advanced 404 error handling example (2)

Continuing with the scenario outlined above, in this section we present another solution to redirect user requests to the correct URL.
When a user sends a request to a page that doesn't exist, like this:
  • /{pageId}/suffix-text
Javascript will redirect the user request to the page:
  • /{pageId}/index.html
The "{pageId}/index.html" object is set with x-amz-website-redirect-location metadata, the value of which is a correct URL address.
  • {pageId}/index.html metadata:
Note: x-amz-website-redirect-location only works in case your CloudFront is connected to S3 Bucket via Website Endpoint.
  • Redirect in S3 Website with x-amz-website-redirect-location
  • Amazon S3 Rest API Endpoint vs S3 Web Endpoint
response-404-advanced-2.html
<!DOCTYPE html>
<html>
<head>
  <title>404 - Page Not Found</title>
  <meta name='referrer' content='no-referrer' />
  <script src="/handle-404-advanced-2.js"></script>
</head>
<body>
   <h1>Sorry, 404 - Page Not Found!</h1>
   Click <a href="/">here</a> to home page.
</body>
</html>
handle-404-advanced-2.java
// Check Page URL and redirect to correct URL If need.
function redirectIfNeed() {
   const currentUrl = window.location.href;
   const currentPathname = window.location.pathname;

   console.log('currentPathname: ' + currentPathname);
   if (currentPathname == '/response-404-advanced-2.html') {
      console.log('Ignore for /response-404-advanced-2.html page');
      return;
   }
   // currentPathname --> /11111/suffix-text
   let j = currentPathname.indexOf('/', 1);
   let pageId = '';
   let suffixText = '';
   if (j == -1) {
      pageId = currentPathname.substring(1);
      suffixText = '';
   } else {
      pageId = currentPathname.substring(1, j);
      suffixText = currentPathname.substring(j + 1);
   }
   console.log('pageId= ' + pageId);
   console.log('suffixText= ' + suffixText);

   let idxPagePath = '/' + pageId + '/index.html';
   if (currentPathname == idxPagePath) {
      console.log('Not Found: ' + idxPagePath);
      return;
   }

   console.log('Wait 3 seconds before redirect to the correct URL...');
   // Wait 3 seconds before redirect to the correct URL
   // --> So you can see the logs in the console
   setTimeout(()=> {
      // Redirect
      window.location.href = idxPagePath;
   } ,3000);
}

redirectIfNeed();
Let's try to see the results: