Tutorial: Front & Back-end web app deployment using AWS S3 and Elastic Beanstalk with SSL/HTTPS

Read on Medium

Having recently deployed two full stack web apps using the MEAN stack (MongoDB, Express, Angular & NodeJS) onto Amazon Web Services (AWS), I can confidently say that despite the generous amount of documentation provided, getting everything production ready on AWS was extremely complicated.

I’m writing this tutorial both as a future reference point for myself, but to also help others navigate some of these complex tasks when deploying on AWS.

For the purposes of this article, two apps are deployed:

  1. The front-end code hosted on AWS S3 as a static site (it’s effectively just a storage bucket); I wrote mine using Angular.
  2. The back-end (or server code) hosted on Elastic Beanstalk. Mine was built with NodeJS and the Express Framework.

I won’t go into the pros/cons of taking a dual approach as opposed to deploying as one app, but you can find plenty of resources discussing this.

Domain Name

Registering a domain name is optional, but if you do, it makes sense in my view to use the AWS service: Route53. The steps are very straight forward so I won’t go into detail. For illustrative purposes, the domain name we’ll register is ‘bounce.com’.

S3 Bucket no.1

You’re going to need two buckets, one with ‘www’ and one without. Once created, both buckets will need public permissions, so turn off ‘Block Public Access’ on the ‘Permissions’ tab.

Create the first bucket without the ‘www’, so just ‘bounce.com’. Enable ‘Static website hosting’ and select ‘Host a static website’. Then type ‘index.html’ for both the index and error document fields; the latter is optional.

You can check you’ve properly uploaded your site’s files by navigating to the bucket url which can be found at the bottom of the ‘Properties’ tab, it looks something like this: ‘http://bounce.com.s3-website.eu-west-2.amazonaws.com’

S3 Bucket no.2

Create a second bucket with the ‘www’ this time, so ‘www.bounce.com’. Enable ‘Static website hosting’ and this time select ‘Redirect Requests for an object’. In ‘Host Name’ enter the domain without ‘www’, so ‘bounce.com’.

Doing this means that if a user types ‘www.bounce.com’ they will be redirected to the first bucket which is where the site’s files are stored.

If you wish to secure your site with an SSL certificate, and let users access it with ‘https’ then check ‘https’ under ‘Protocol’. This is highly recommended to avoid browsers blocking your website, and it’s free if you register your domain with AWS Route53.

That’s the main steps complete on the S3 side, you won’t be able to access your site using your registered domain name yet though until we’ve set up Route53 Hosted Zones.

Elastic Beanstalk

This is where we’ll deploy the backend for our app, it’s essentially a bunch of files which contain our server side code and API endpoints which in my case allow the client to send and request data to and from my database.

Create a new ‘Web Server Environment’, enter the app and environment name. Choose your platform (I used NodeJS and all default settings).

Under ‘Application Code’ select ‘Upload your code’ and select the .zip file which contains your backend files. The setup of this can take a few minutes and it’s all done in the background, so you can refresh the page as much as you like.

Take note of the url of this EB app, you’ll need it when setting up Route53. It looks something like: ‘bounce.eu-west-2.elasticbeanstalk.com’

Route 53

Create the first ‘Hosted Zone’ with the following settings:

Domain Name: bounce.com (without the ‘www’)
Type: Public Hosted Zone

By default you’ll see two records: ‘NS’ and ‘SOA’, but we need to add a few more:

Record Name: bounce.com
Type: ‘A’
Alias: to S3 website endpoint, choose your region then select the bucket you created (the one without ‘www’)

Record Namewww.bounce.com
Type: ‘A’
Alias: to S3 website endpoint, choose your region then select the bucket you created (the one with ‘www’)

Create your second ‘Hosted Zone’ with the following settings:

Domain Nameapi.bounce.com
Type: Public Hosted Zone

Add the following record:

Record Nameapi.bounce.com
Type: ‘A’
Alias: to the Elastic Beanstalk url you should have noted down earlier

We need to add the auto-generated ‘NS’ records from this Hosted Zone to the main one we created first. So copy the four lines of ‘ns-xxx’ addresses, go to the first Hosted Zone, create a new record with ‘api.bounce.com’ as the name, select ‘NS’ as the record type then paste the four lines into the ‘Value’ box then hit save.

Certificate Manager

Adding these certificates means that users can visit your website using ‘HTTPS’, and is a requirement by most modern browsers.

You’ll need to create two SSL certificates: one for your front-end and one for the back-end, I decided to use api.bounce.com’ as the sub-domain for my back-end.

For the first certificate, click ‘Request a Certificate’ and put ‘bounce.com’ as the domain name, then click ‘add another name to this certificate’ and enter ‘www.bounce.com’, choose ‘DNS Validation’ and then click ‘Review’.

On the next screen you should have the option to create the CNAME record in Route 53 at the click of a button. You’ll need to do this for both domains, the one with ‘www’ and the one without.

If you go back to Route 53 and open up the Hosted Zone you created, you should now see two CNAME records with long strings produced by your SSL certificate. If you can’t see these two new records, double check the steps or manually add them.

Create a certificate for the ‘www’ version of the site too.

For the second certificate, put ‘api.bounce.com’ as the domain name, no additional name needed, choose DNS validation and you’ll be able to click to create the CNAME record in Route53 at the click of a button again. This time, the record will be added to the ‘api.bounce’ Hosted Zone. Again, feel free to check this has been correctly added.

Make sure you create the api certificate in the same region as your Elastic Beanstalk environment. This is different to the other certificates which need to be created in North-Virginia to be picked up by Route53

You should now have 4 records present in the ‘api.bounce.com’ Hosted Zone, and 7 in the main ‘bounce.com’ one.

Elastic Beanstalk

By now our EB environment should be set up, so open it up and navigate to ‘Configuration’ in the left pane. Scroll down to ‘Load Balancer’ and click ‘Edit’.

Add a new listener with the following settings:

Port: 443
Protocol: HTTPS
SSL Certificate: Select the certificate you created earlier: api.bounce.com’
SSL Policy: I chose the first on the list, I’m not sure which is best.

CloudFront Distribution

Create your first Distribution, the following settings are key (the rest can be left as default):

Origin Domain Name: This is the S3 bucket endpoint from earlier, the one without ‘www’. It should look something like: ‘ytworkouts.com.s3-website.eu-west-2.amazonaws.com’, worth noting that this may not be what is suggested by the form field which seems to omit the word ‘website’. It took me forever to figure this out.
Viewer Protocol Policy: Redirect HTTP to HTTPS
Alternate Domain Names: ‘bounce.com’ (no ‘www’)
Custom SSL: Select the ‘SSL’ certificate which starts with ‘bounce.com’. Another important point is that this certificate needs to have been created in the ‘North Virginia’ region for it to appear.
Default Root Object‘index.html’

Create a second Distribution, with the following settings:

Origin Domain Name: Like before but this time it’s the S3 bucket endpoint which starts with ‘www’.
Viewer Protocol Policy: Redirect HTTP to HTTPS
Alternate Domain Nameswww.bounce.com’
Custom SSL: Select the ‘SSL’ certificate which starts with www.bounce.com’.

Route 53

Now we’ve got our CloudFront Distributions set up, we need to go back to Route53 and amend two records.

Open the ‘bounce.com’ Hosted Zone, click to edit the bounce.com ‘A’ record and instead of routing traffic to the S3 endpoint, we’ll route it the CloudFront Distribution we just created, when you select this the relevant address should appear, it looks something like ‘xxx.cloudfront.net’. Hit save.

Do the same for the other ‘A’ record with the name www.bounce.com’. When selecting the CloudFront Distribution address just make sure it’s different to the other one we just set.

To summarise, we’ve set up:

  • Two S3 buckets to host our front-end, one with ‘www’ and one without
  • Elastic Beanstalk (EB) to host our back-end and have secured this with SSL thanks to the Load Balancer feature.
  • One domain name
  • Two Hosted Zones, one for the main domain and one for the ‘api’ sub-domain which re-directs to the EB environment
  • Two CloudFront Distributions, one with ‘www’ and one without and set these to re-route HTTP requests to HTTPS

NB: don’t forget to make sure the back-end url referred to in your code is the new ‘api.bounce.com’ sub-domain and not the long Elastic Beanstalk address.

NB: you don’t have to use SSL and can let your users access both your front-end domain and backend APIs via ‘HTTP’ only. If you choose to do this, then you don’t need to set up CloudFront distributions. The reason I had to create a sub-domain ‘api.bounce.com’ which would route to my EB environment is because if you apply SSL on the front-end but not the back-end then you’ll get a mis-match error when attempting to make your API calls which because your main domain is ‘HTTPS’ and the back-end is ‘HTTP’. So all the steps involving ‘api.bounce.com’ are included to ensure SSL is present on the back-end and we couldn’t just apply the SSL to the default EB address, it needs to be on our custom domain.

As you can see there’s quite a few steps and it’s easy to get lost so hopefully this helps alleviate some of the pain. Happy to answer any questions down in the comments.

This is an example of full stack web app which I deployed on AWS using the above method: https://ytworkouts.com