Error handling is the secret sauce that drives modern websites. Drupal and other Model-View-Controller (MVC) web frameworks are driven by 404 handlers. If you go to http://website.com/some/url the web server is probably configured to check to see if "/some/url" exists, and if it does not it will route you to a default page. This default page is a controller that will parse "/some/url" and figure out what to serve.

In my previous post on separating content creation and delivery I advocated using a static Single Page Application (SPA) as a front end to a site. When I sat down to actually implement that approach I quickly ran into the challenge of what to do about handling static URLs. I know that Drupal handles it through webserver configuration and needed something similar for my site. The solution is AWS CloudFront customized error responses. Using AWS CloudFront, I configured any URL requested to return my Single Page Application. That code will then handle the parsing of the URL and return the correct content.

Pre-Setup

I already know I am going to be using the Amazon Web Services suite to do this project and keeping my code safe in a github repo. I also use a helper ruby gem called s3_website that I'm a big fan of. To get started I did a few things:

  • Installed git and the s3_website gem.
  • Created a new github repo and cloned it locally.
  • Created a AWS IAM User with AmazonS3FullAccess and CloudFrontFullAccess policies.
  • In my local environment ran the s3_website cfg create to create an empty config.
  • Setup the s3_website.yml to have my AWS IAM User keys and the name of the S3 bucket. You should name the bucket after the URL you are going to use for the site and follow S3 bucket naming conventions that also conform with DNS requirements.

Then I ran s3_website cfg apply to create the S3 bucket CloudFront distribution. The ouput looks like this:

$ s3_website cfg apply
Applying the configurations in s3_website.yml on the AWS services ...
Created bucket spa.digitalambit.com in the US Standard Region
Bucket spa.digitalambit.com now functions as a website
Bucket spa.digitalambit.com is now readable to the whole world
No redirects to configure for spa.digitalambit.com bucket
Do you want to deliver your website via CloudFront, the CDN of Amazon? [y/N]
y
  The distribution ASDFASDF at asdfasdfcloudfront.net now delivers the origin spa.digitalambit.com.s3-website-us-east-1.amazonaws.com
    Please allow up to 15 minutes for the distribution to initialise
    For more information on the distribution, see https://console.aws.amazon.com/cloudfront
  Added setting cloudfront_distribution_id: ASDFASDF into ./s3_website.yml

A Simple Single Page Application

To create the basic framework for a site I used Initializr which provides a JQuery/Bootstrap CSS HTML5 boilerplate site. It makes it really easy for non-front-end developers like myself to get a site up quickly.

I dropped this in a folder in my local repo called "site" and then change my s3_website.yml to have "site: site" so the gem knows my files are in the site folder.

I then added a very simple example of how a javascript file could respond to the URL of the page it is being displayed by taking the current URL and shoving it in an element on the page.

    $('#this_url').html(window.location.href);

Then I pushed all this up to the s3 bucket using s3_website push.

DNS Configuration

After this, I went into the CloudFront configuration and setup an "Alternate Domain Name" and went into Route 53 DNS settings and created a CNAME that points spa.digitalambit.com at the CloudFront distribution. This isn't necessary, but I like the pretty URLs better than the machine-generated ones that CloudFront and S3 use by default.

The CloudFront/S3 Configuration Magic

At this point in the project I have a site that responds only to the index url. If I go to spa.digitalambit.com I get my simple SPA, but if I go to spa.digitalambit.com/blah I get a nasty error message because blah doesn't exist. This is where the magic of CloudFront error handling will make the whole project work. In the settings for the CloudFront distribution there is an Error Pages tab with a "Create Custom Error Response" button.

screen shot of cloudfront custom error response

I setup the 404 response code to respond with the index.html and to respond with a "200: OK" response code. I also change the cache to 1 day. This tells CloudFront that if it can not find an asset on the origin S3 Bucket, to go ahead and return the index.html anyway.

Results

Once the CloudFront settings propogate all my requests spa.digitalambit.com/ that do not match an existing object in the S3 bucket get routed to the index.html. This replicates the web server configuration we are used to in Drupal and Wordpress at the CDN level. If you go to spa.digitalambit.com/blah you get the same index.html as you would if you went to the index page.

Next Steps

This was a precursor to my overall project which is to host a Single Page Application that is driven by Drupal and Elastic Search. The next steps are to make this Single Page Application actually talk to a back-end system that has content.

I have a newsletter...

Many of my posts end up in Digital Ambit's monthly newsletter. It is the best way to keep up with what Dagny and I are doing in the business world. I appreciate your support and will only send you things we think are valuable.