Publish and maintain SPA on S3 (with HTTPS)

Łukasz Pawłowski
The Startup
Published in
8 min readNov 19, 2019

There are a lot of hosting providers. If you need to host a single page application or static page (for example created with GatsbyJS or gridsome), you do not need anything fancy. You just need file storage with the possibility to set a custom domain. Also if you want to make your page public you should include SSL. Today we’ll check how to do it with AWS.

Let’s assume that we set up everything on AWS. We’ll deploy vue.js single page application. We’ll use a few services:

  • S3, to host files
  • Route 53, to manage our domains
  • CloudFront for CDN and to provide SSL for S3 resources
  • Certificate Manager to provide SSL certificate

After we set up our page, we’ll take a look at updating our application. Also, we’ll check how we can automate some of these tasks.

Set up bucket

First, we need a new bucket, where we’ll host our page. We’ll use the root of the bucket as the root folder. I’ll use eu-west-1 region.

We find S3 service and add a new bucket. We need to adjust bucket’s name to CloudFront requirement for SSL — bucket can’t contain dots (.). Most of the things you can set up when you create a bucket, but we’ll present each setting as a separate step. The first step is just a bucket’s creation.

bucket creation — we just set bucket name and region

Now, when bucket exists, we need to set up permissions. We’ll set read access to our bucket for everyone. First, we deactivate blockade for public access. This block secure as before unauthorized and accidental changes on bucket policy, ACL, etc. But also we are not able to set any public access on the bucket.

Unblock public access

When we are able to configure public access, we create policy:

Set policy for your bucket

Here is code version, that you can copy and pass (remember to change your-bucket-name to the name of your bucket):

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::your-bucket-name/*"
]
}
]
}

Now you need to upload your SPA on S3. You should upload there build version of application. Lets assume that we created app already. We can run locallynpm run build and copy everything from dist folder into root of our bucket. If you use vue-cli you can use vue-cli-plugin-s3-deplyo not only to publish on s3 but also for other steps described in this article.

Root folder of bucket is equal to your dist vue folder

Ok, we have code in place. Here little side note — if you do not use SSL you also need to use static website hosting setting. If you use SSL do not set it up, because we’ll not have direct access to the bucket. Bucket mechanism does not support SSL, so we need to do it another way. We’ll use CloudFront for that. Also if you do not need SSL and want to use build-in s3 static website hosting you need to name your bucket the same way as your domain. We do not use that option.

Set up domain

We’ll use route 53 for routing and domains. You can buy a new one or use an existing one. Here I’ll use domain which is already managed on route 53, so I do not need to do anything. Ordering domain can take some time. We’ll use main domain.

Get SSL certificate

When we have domain we need certificate to use https. Remember that we’ll use CloudFront to access our page. Currently, CloudFront support only SSL certificate created in region N. Virgin. This is why we switch to that region and create a certificate request.

Remember to create certificate in N. Virginia region

First thing is to declare domain. We’ll go with domain, which we configured in route 53.

After we click through, we’ll need to confirm ownership of the domain. We select verification by DNS.

Select the option to verify with DNS

Now we are presented with information about records that we should put in our DNS server. Because we are using route53 we are able to publish record with one click.

Set up your DNS

When our domain is confirmed, our certificate is ready to use.

Make sure, that your certificate is Issued before moving to the next steps

Set up CloudFront

At that point we have our code on S3, domain configured on route 53 and valid SSL for our subdomain, which we will use. Now we can set up CloudFront. Find CloudFront service and create a new distribution. Select Web delivery method:

Select Web delivery method

We need to set a few things:

  • Origin Domain Name — need to direct to our bucket;
  • Alternate Domain Names — we need to add here our domain, for which we created certificate
  • SSL Certificate — we’ll use custom SSL certificate, which we created in N. Virginia region
  • Default Root Object — here we have index.html
Create CloudFront Distribution

Now we need to wait until the system finish deployment (can take over 20 minutes). In the last step we will need to set DNS. We get to route 53 service and select our hosted zone. We add new record A as alias for our CloudFront.

Add DNS record to direct domain to CloudFront

Now we can check if our application work.

Adding SSL to s3 bucket with static website hosting

What if you already have s3 bucket and static hosting configured, but you want to add SSL? Well, it is not that easy. There are a few difference:

  • S3 bucket does not support SSL. You need to use other services, which will reuse S3. You can use CloudFront. To set it up you can follow the steps described above.
  • For custom domains, you have already DNS configured. You will need to change records and redirect traffic to CloudFront.
  • Your bucket name is exactly the same as domain and you already turned on static website hosting. Your bucket name is incompatible with CloudFront’s requirements because it includes period (.). You also can’t change the name of an existing bucket. You need to create new and copy all data. In new bucket do not configure static website hosting — CloudFront will take care of this

So to summarized steps:

  1. Create a new bucket with a copy of all data from the old one
  2. Create SSL certificate
  3. Configure CloudFront
  4. change DNS

Updating application

Congratulations you just published your first SPA app on S3 and added SSL to it. Sooner or later you’ll need to update your app. If you used basic S3 static page web hosting, you just pushed a new version of files to S3 bucket. When you use SSL with CloudFront it is not enough.

First of all, you still need to update files on S3. When files are updated, the new version would not be automatically available under your domain. CloudFront is CDN. By default, all resources are cached for 24 hours. There are two options to solve that problem: object versioning or object invalidation. We’ll focus here on object invalidation.

To invalidate object you need to open CloudFormation distribution page. There you’ll find the Invalidations tab. Create new invalidation. You can put the path for all updated files or use a wildcard to invalidate the whole bucket:/*. Keep in mind, that it is case sensitive.

Invalidate your CloudFormation distribution after updating code in S3

You’ll need to wait until all caches in all regions updated. After that verify your page, if changes are available.

Automation

There are two things, which you can try to automate: setting up a new page or update the page.

For setting up page you can use CloudFormation to configure your stack. But there are a few problems, for example:

  • You are not able to confirm your domain with basic CloudFormation. For that, you would need to write some lambda functions.
  • You are not able to upload files to S3 (unless you use SAM). You can pass S3 access in output and use your own scripts to publish data on S3.

Generally, if you do not have too many projects it is faster to configure AWS by a hand.

As for updates, you can write some script with any tool you like. I keep code on git and track merges with Jenkins. Jenkins run bash script.

I build docker which includes AWS CLI and I configured CLI to use my account. I use docker because I have multiple projects with multiple settings, so I prefer to keep everything isolated. When Jenkins run update action, it connects via ssh with my deployment server where Docker container lives. First I pull changes from git and then run script on docker. Script contains only a few simple commands. Here is the skeleton for it:

cd /var/www
npm install
npm run build
aws s3 cp ./dist s3://$bucketName --recursive
aws cloudfront create-invalidation --distribution-id $distributionId --paths /*

Distribution id can be found in your AWS Console:

You’ll need distribution id to invalidate your resources with AWS CLI

That’s it. I hope you liked it and that it explains how to use AWS to host your SPA or static pages.

--

--

Łukasz Pawłowski
The Startup

Web developer, tech advisor, manager, husband & father. Tech Manager at Boozt